Trouble mapping an Array of data to Images in React - javascript

I have a map that looks like this
I am using .map() to try to produce them as images like so:
{theLinks.map((item, indx) => (
<img key={indx} src={item.src} alt={item.label} />
))}
Nothing is getting returned, if I mess with it I can get a single img to return with no valid source and the alt is "unknown".

make sure to add your mapped array theLinks after or inside a return function,
for example, this will NOT work :
export default function App() {
const theLinks = [
{ lable: "Daily Mix", src: "https://flif.info/example-images/fish.png" },
{ lable: "Legit", src: "https://flif.info/example-images/fish.png" },
{ lable: "SCL", src: "https://flif.info/example-images/fish.png" }
];
{theLinks.map((item, indx) => (
<img
style={{ width: "50%", border: "1px solid #ccc" }}
key={indx}
src={item.src}
alt={item.label}
/>
))}
}
SOLUTION 1
this will work (renders the mapped array and other elements too):
export default function App() {
const theLinks = [
{ lable: "Daily Mix", src: "https://flif.info/example-images/fish.png" },
{ lable: "Legit", src: "https://flif.info/example-images/fish.png" },
{ lable: "SCL", src: "https://flif.info/example-images/fish.png" }
];
return (
<>
<h1> thanks Phil for your suggestion! </h1>
{theLinks.map((item, indx) => (
<img
style={{ width: "50%", border: "1px solid #ccc" }}
key={indx}
src={item.src}
alt={item.label}
/>
))}
;
</>
);
}
SOLUTION 2
this will work (renders only the mapped array)
export default function App() {
const theLinks = [
{ lable: "Daily Mix", src: "https://flif.info/example-images/fish.png" },
{ lable: "Legit", src: "https://flif.info/example-images/fish.png" },
{ lable: "SCL", src: "https://flif.info/example-images/fish.png" }
];
return theLinks.map((item, indx) => (
<img
style={{ width: "50%", border: "1px solid #ccc" }}
key={indx}
src={item.src}
alt={item.label}
/>
));
}

Try this
{theLinks.map((item, indx) => { return ( <img key={indx} src={item.src} alt={item.label} /> ); })}

Related

How can I create a menu with my json info with REACT

what I try to do is to have the same display as this picture :
So in my menu the plant type (Type of plant1) is displayed above a gray bar and when you click on the down chevron then you can see all the plants name, related to this type, with checkboxes on left, by default there will be all checked. And the blue rectangle indicates the number of plants that have been selected.
How can I do that, which package can help me in REACT?
Here my plants.json :
{
"plants_type": [
{
"_id_type": "1",
"name_type": "Type of plant1",
"plants": [
{
"name": "Plant1.1",
"_id": "2"
},
{
"name": "Plant1.2",
"_id": "3"
}
]
},
{
"_id_type": "4",
"name_type": "Type of plant2",
"plants": [
{
"name": "Plant2.1",
"_id": "5"
},
{
"name": "Plant2.2",
"_id": "6"
}
]
}
]
}
You can create a dropdown list on your own like below. I have added the logic of selecting items to the data itself.
You can keep a component called Category to keep a single state of the parent menu item. Whether it's open or not. Then iterate over the plants as checkbox inputs to make them selectable.
I have used a simple initialize function to make all the items selected initially. This should work as you expect. Add a console log of selectionMenu to see how selected property changes while toggling items.
Move the inline styles to CSS classes to make the code more clear.
const data = { plants_type: [ { _id_type: "1", name_type: "Type of plant1", plants: [ { name: "Plant1.1", _id: "2" }, { name: "Plant1.2", _id: "3" } ] }, { _id_type: "4", name_type: "Type of plant2", plants: [ { name: "Plant2.1", _id: "5" }, { name: "Plant2.2", _id: "6" } ] } ] };
const Category = ({ _id_type, name_type, plants, changeSelection }) => {
const [toggleState, setToggleState] = React.useState(false);
return (
<div key={_id_type}>
<div
style={{
cursor: "pointer",
userSelect: "none",
display: "flex",
margin: "2px",
backgroundColor: "lightgray"
}}
onClick={() => setToggleState((prev) => !prev)}
>
<div>{name_type}</div>
<div
style={{
backgroundColor: "blue",
color: "white",
padding: "0px 10px",
marginLeft: "auto"
}}
>
{plants.filter(({ selected }) => selected).length}
</div>
</div>
<div style={{ marginLeft: "10px" }}>
{toggleState &&
plants.map(({ name, _id, selected }) => (
<div key={_id}>
<input
key={_id}
type="checkbox"
value={name}
checked={selected}
onChange={(e) => changeSelection(_id_type, _id, e.target.value)}
/>
{name}
</div>
))}
</div>
</div>
);
};
const App = () => {
const initializeSelectionMenu = (data) => {
return data.map((item) => {
return {
...item,
plants: item.plants.map((plant) => ({ ...plant, selected: true }))
};
});
};
const [selectionMenu, setSelectionMenu] = React.useState(
initializeSelectionMenu(data.plants_type)
);
console.log(selectionMenu);
const changeSelection = (catId, itemId, value) => {
setSelectionMenu((prevSelectionMenu) =>
prevSelectionMenu.map((item) => {
if (item._id_type === catId) {
return {
...item,
plants: item.plants.map((plant) => {
if (plant._id === itemId) {
return { ...plant, selected: !plant.selected };
}
return plant;
})
};
}
return item;
})
);
};
return (
<div>
{selectionMenu.map((item) => (
<Category
{...item}
changeSelection={changeSelection}
key={item._id_type}
/>
))}
</div>
);
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>

Iterate over JS object of arrays

I have this object that I'm trying to loop over in a form but really can't get it to work. Here is sample of the object.
const data = {
"Social Security": [
{
label: "Deduction Type",
value: "Social Security",
name: "SocialSecurity"
},
{
label: "Employer Rate",
value: "12.4%",
name: "SocialEmployer"
},
{
label: "Employee Rate",
value: "6.2%",
name: "SocialEmployee"
}
],
"Medicare": [
{
label: "Deduction Type",
value: "Medicare",
name: "Medicare"
},
{
label: "Employer Rate",
value: "1.45%",
name: "MedicareEmployer"
},
{
label: "Employee Rate",
value: "2.9%",
name: "MedicareEmployee"
}
]
}
form implementation
<Formik>
{({ values, isSubmitting, resetForm, setFieldValue }) => (
<Form id="payrollSettingsForm" >
<Grid container className={classes.border}>
<Grid item xs={12} md={4}>
{Object.entries(data).map(arr =>{
Array.isArray(arr) && arr.map(elm =>{
return (<TextField
label={elm.label}
value={elm.value}
name={elm.name}
/>)
})
})}
</Grid>
</Grid>
...rest of the form
</Form>
</Formik>
Tried many approaches like Object.fromEntries(). forEach I think all the methods I can find example and keep failing, maybe I'm doing something wrong, any help will be appreciated.
{Object.entries(data).map(([key, val]) =>{
return val.map(elm =>{
return (
<TextField
label={elm.label}
value={elm.value}
name={elm.name}
/>
)
})
})}
Map should return an element on each iteration. In case you don't want to display return null else return a react node.
<Grid item xs={12} md={4}>
{Object.entries(data).map(arr => {
Array.isArray(arr) ? arr.map(elm => {
return (<TextField
label={elm.label}
value={elm.value}
name={elm.name}
/>)
}) : null
})}
</Grid>

Array Map method not rendering properly in React

I am using ChakraUI and React to make a project where I want to show a list of members in the form of cards.
But for some reason the component is not rendering properly. Only the Team member text is showing in the browser. But the part where I mapped the listOfTeamMembers array, is not rendering. What is the reason behind it?
Member component
import { Box, Text, Center, Stack } from "#chakra-ui/layout";
import listOfTeamMembers from "./team-members-list";
function TeamMembers() {
return (
<Box id='team-members' mt={10}>
<Box>
<Center>
<Text fontSize='4xl' as='u' fontFamily='sans-serif'>
Team Members //only this part is showing on website
</Text>
</Center>
<Box>
{listOfTeamMembers.map((items) => {
<Stack
spacing={{ base: "18px", md: "25px", lg: "40px" }}
direction='row'
>
<Box
w={{ base: "300px", md: "200px", lg: "100px" }}
h={{ base: "300px", md: "200px", lg: "100px" }}
borderRadius={5}
>
{items.name}
{items.contact}
{items.details}
</Box>
</Stack>;
})}
</Box>
</Box>
</Box>
);
}
export default TeamMembers;
The member array
const listOfTeamMembers = [
{
name: "Dr. DoofenShmirtz",
contact: "#",
details:
"The evil genius and a loving father of one daughter. Hate Perry the platipus",
},
{
name: "Dr. Frankestein",
contact: "#",
details: "I experiment on Dead People",
},
{
name: "Dr. Drake Ramoray",
contact: "#",
details: "Has magical hands. Can do brain Transplant without any Help",
},
{
name: "Dr. House MD",
contact: "#",
details: "I am a fictional Character. What am I doing here!",
},
{
name: "Dr. Hibbert",
contact: "#",
details: "ohhohohohohohoho",
},
{
name: "Dr. Elmer Hartman",
contact: "#",
details: "My role as a doctor is to cure people. But I do the opposite.",
},
];
export default listOfTeamMembers;
My App.js component
import { ChakraProvider } from "#chakra-ui/provider";
import { useState } from "react";
import Navbar from "../src/components/navbar";
import Home from "./components/Home";
import theme from "./theme";
import TeamMembers from "../src/components/Team Members/team-members";
function App() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
<ChakraProvider theme={theme}>
<Navbar isMenuOpen={isMenuOpen} setIsMenuOpen={setIsMenuOpen} />
<Home isMenuOpen={isMenuOpen} />
<TeamMembers />
</ChakraProvider>
);
}
export default App;
You have to return array item also :-)
{listOfTeamMembers.map((items) => {
return (
<Stack
spacing={{ base: "18px", md: "25px", lg: "40px" }}
direction="row"
>
<Box
w={{ base: "300px", md: "200px", lg: "100px" }}
h={{ base: "300px", md: "200px", lg: "100px" }}
borderRadius={5}
>
{items.name}
{items.contact}
{items.details}
</Box>
</Stack>
);
})}
Here is demo: https://codesandbox.io/s/sharp-ganguly-lcj38?file=/src/App.js:1122-1694

React Flow: How to remove an element?

I'm having issues with deleting elements in react-flow via a button.
I can delete elements fine using Backspace but the button only works for the first delete and after that it brings back the deleted node.
New to using react-flow and can't put my finger on the problem here. Is the state not getting changed some how?
Below is the code I use for react flow
CodeSandbox here.
import React, { useState, useCallback, useEffect } from "react";
import ReactFlow, {
removeElements,
addEdge,
Background,
} from "react-flow-renderer";
const onLoad = (reactFlowInstance) => {
reactFlowInstance.fitView();
console.log(reactFlowInstance.getElements());
};
const StackFlow = () => {
const initialElements = [
{
id: "0",
position: { x: 0, y: -100 },
sourcePosition: "bottom",
style: {
width: 100,
fontSize: 11,
color: "white",
background: "#6ec9c0",
},
data: {
label: (
<>
<button
className="w-md h-md border-2 border-black p-2"
onClick={() =>
remModelData("0")
}
>
Del
</button> <br /> <br />
<strong>Models</strong>
</>
),
},
},
{
id: "1",
position: { x: 100, y: 50 },
sourcePosition: "bottom",
targetPosition: "top",
data: {
label: (
<>
<button
className="w-md h-md border-2 border-black p-2"
onClick={() =>
remModelData("1")
}
>
Del
</button> <br /> <br />
Model: <strong>1</strong> <br />
ID: 1
</>
),
},
},
{
id: "2",
position: { x: 150, y: 250 },
sourcePosition: "bottom",
targetPosition: "top",
data: {
label: (
<>
<button
className="w-md h-md border-2 border-black p-2"
onClick={() =>
remModelData("2")
}
>
Del
</button> <br /> <br />
Model 1: <strong>subModel1</strong> <br />
ID: 2
</>
),
},
},
{
id: "3",
position: { x: 250, y: 250 },
sourcePosition: "bottom",
targetPosition: "top",
data: {
label: (
<>
<button
className="w-md h-md border-2 border-black p-2"
onClick={() =>
remModelData("3")
}
>
Del
</button> <br /> <br />
Model 1: <strong>subModel2</strong> <br />
ID: 3
</>
),
},
},
{
id: "0-1",
type: "step",
source: "0",
target: "1"
},
{
id: "1-2",
type: "step",
source: "1",
target: "2"
},
{
id: "1-3",
type: "step",
source: "1",
target: "3"
},
];
const [elements, setElements] = useState(initialElements);
const onElementsRemove = useCallback(
(elementsToRemove) =>
setElements((els) => removeElements(elementsToRemove, els)),
[]
);
const onConnect = (params) => setElements((els) => addEdge(params, els));
const [reactflowInstance, setReactflowInstance] = useState(null);
useEffect(() => {
if (reactflowInstance && elements.length > 0) {
reactflowInstance.fitView();
}
}, [reactflowInstance, elements.length]);
const remModelData = useCallback((id) => {
let arr = elements
var index = arr.indexOf(id);
if (index > -1) {
arr.splice(index, 1);
}
arr = arr.filter(function (obj) {
return obj.id !== id;
});
console.log(arr);
setElements(arr);
}, []);
return (
<ReactFlow
elements={elements}
onElementsRemove={onElementsRemove}
onConnect={onConnect}
onLoad={onLoad}
snapToGrid={true}
snapGrid={[15, 15]}
>
<Background color="#aaa" gap={16} />
</ReactFlow>
);
};
function Home() {
return (
<>
<div className="w-full mx-auto justify-center items-center flex">
<div
className="flex mt-10 flex-row items-center content-center justify-center max-h-5xl max-w-5xl py-2"
style={{ flex: 1, width: 1000, height: 800, borderWidth: 2 }}
>
<StackFlow />
</div>
</div>
</>
);
}
export default Home;
I was able to accomplish the end result I wanted by creating this function:
const deleteNode = (id) => {
setElements((els) => removeElements([elements[id]], els));
};
And passing this into the OnClick:
onClick={() => deleteNode(0)}
Changing 0 for what ever index the element you wish to remove from the array is
As from version 10 Elements has been replaced with Nodes and Edges.
I could achieve the following using the function below.
const deleteNodeById = (id) => {
flowInstance.setNodes((nds) => nds.filter((node) => node.id !== id))
}
And passing this function to my delete button (In my case mui delete icon)
<DeleteOutlined
onClick={() => deleteNodeById(data.id)}
style={{ color: '#FF0000' }}
/>
As of version 11.2 onwards, you can use
reactFlowInstance.deleteElements({ nodes: nodesToDelete, edges: edgesToDelete });
The advantage is that method will fire "onNodesDelete" & "onEdgesDelete" events so that you can reuse your handlers.
Recall that these events are also fired if you press the "backspace" button.

How can I setState from inside a render function in react

i know you are supposed to keep render functions pure but I have a special case where I need to pass some values and update the state inside a render function but i am getting the following error message:
Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state
Code:
import Carousel from 'react-elastic-carousel';
import top from '../images/top.png';
import ArrowRight from '../images/arrowRight.svg';
import React, {Component} from 'react';
class contentSlider extends Component {
state = {
disabled: '',
leftArrow: false,
rightArrow: true
}
sliderData = [
{
title: 'POLO',
typeOfcontent: 'Mens T-Shirt',
rrp: '£105',
ourPrice: '£55',
salePrice: '£45',
image: top
},
{
title: 'POLO',
typeOfcontent: 'Mens T-Shirt',
rrp: '£105',
ourPrice: '£55',
salePrice: '£45',
image: top
},
{
title: 'POLO',
typeOfcontent: 'Mens T-Shirt',
rrp: '£105',
ourPrice: '£55',
salePrice: '£45',
image: top
},
{
title: 'POLO',
typeOfcontent: 'Mens T-Shirt',
rrp: '£105',
ourPrice: '£55',
salePrice: '£45',
image: top
},
{
title: 'POLO',
typeOfcontent: 'Mens T-Shirt',
rrp: '£105',
ourPrice: '£55',
salePrice: '£45',
image: top
},
{
title: 'POLO',
typeOfcontent: 'Mens T-Shirt',
rrp: '£105',
ourPrice: '£55',
salePrice: '£45',
image: top
},
{
title: 'POLO',
typeOfcontent: 'Mens T-Shirt',
rrp: '£105',
ourPrice: '£55',
salePrice: '£45',
image: top
}
]
breakPoints = [
{ width: 2, itemsToShow: 2, itemsToScroll: 2 },
{ width: 550, itemsToShow: 3, itemsToScroll: 3},
{ width: 850, itemsToShow: 4, itemsToScroll: 3 },
{ width: 970, itemsToShow: 4, itemsToScroll: 3 },
{ width: 1150, itemsToShow: 5, itemsToScroll: 3 },
]
setDirection = (slideDirection) => {
switch(slideDirection) {
case "next":
this.carousel.slideNext();
let slideNext = document.getElementById('slider-move');
if(slideNext.classList.contains('test-right')) {
slideNext.classList.remove('test-right');
slideNext.classList.add('test-left');
}
break;
case "previous":
this.carousel.slidePrev();
let slidePrevious = document.getElementById('slider-move');
if(slidePrevious.classList.contains('test-left')) {
slidePrevious.classList.remove('test-left');
slidePrevious.classList.add('test-right');
}
break;
}
}
getAmountOfPages = (pages, activePage ) => {
console.log(activePage)
let firstItem = pages[0];
let [lastItem] = pages.slice(-1);
if(firstItem === activePage) {
this.setState({
leftArrow: false,
rightArrow: true,
})
} else if(lastItem === activePage) {
this.setState({
leftArrow: true,
rightArrow: false,
})
} else {
this.setState({
leftArrow: true,
rightArrow: false,
})
}
// get first item in the array and compare it to the active page
// get the last element in the array and compare it to the active page
}
render() {
return (
<div className="content-slider-wrapper">
<div className="content-slider-title">
<span>PRODUCTS OF THE WEEK</span>
</div>
<div className={`${this.sliderData.length === 5 ? 'd-xl-none' : ''} arrow-container`}>
<img onClick={() => this.setDirection("previous")} className="arrow-left" src={ArrowRight} />
<img onClick={() => this.setDirection("next")} src={ArrowRight} />
</div>
<div className={`${this.sliderData.length === 5 ? 'mt-xl-5' : ''} content-slider-container`}>
<div className="test-right" id="slider-move">
<Carousel
ref={ref => (this.carousel = ref)}
breakPoints={this.breakPoints}
disableArrowsOnEnd={true}
renderPagination={({ pages, activePage, onClick }) => {
this.getAmountOfPages(pages, activePage);
return (
<div className={`${this.sliderData.length === 5 ? 'd-xl-none' : ''} black-slider-container`}>
{pages.map(page => {
const isActivePage = activePage === page
return (
<div className={isActivePage ? 'black-slider' : 'blank-slider'}
key={page}
onClick={() => onClick(page)}
active={isActivePage}
/>
)
})}
</div>
)
}}
>
{this.sliderData.map((item, index) => (
<div key={index} className="carousel-item-container">
<div className="carousel-image-container">
<img src={top} />
</div>
<div className="carousel-text-container">
<ul>
<li className="carousel-text-container-title">{item.title}</li>
<li className="carousel-text-container-text">{item.typeOfProduct}</li>
<li className="carousel-text-container-text line-through">RRP {item.rrp}</li>
<li className="carousel-text-container-text line-through">Our Price: {item.ourPrice}</li>
<li className="carousel-text-container-text">Sale Price: {item.salePrice}</li>
</ul>
</div>
</div>
))}
</Carousel>
</div>
</div>
</div>
)
}
}
export default contentSlider;
I need to call this function
this.getAmountOfPages(pages, activePage);
which is inside my render to update the state when needed however it dont work any ideas....
I am Brazilian and therefore I speak Portuguese, but I will use the translator to try to help you.
By calling this function the way you are trying to do it, you would fall into an infinite loop. Look:
You update the state inside the render () method;
The render () method is called due to the state update;
The render method updates the state;
all over again
I've already used this library, so I'll show you how I did it:
<Carousel
itemsToShow={7}
renderArrow={renderArrow}
enableMouseSwipe={false}
pagination={false}
breakPoints={[
{
width: 450,
itemsToShow: 3,
enableSwipe: true,
enableMouseSwipe: true,
itemsToScroll: 3,
renderArrow: () => <div />,
},
{
width: 500,
itemsToShow: 3,
},
{ width: 620, itemsToShow: 4 },
{ width: 720, itemsToShow: 5 },
{ width: 850, itemsToShow: 6 },
{ width: 1150, itemsToShow: 7 },
]}
>
{products &&
products.map((p) => (
<div key={p.docId} className="products-prod">
<img
onClick={() => addProductCart(p)}
alt={p.descricao}
src={`data:image/png;base64,${p.thumbnail}`}
className={`shadow2 ${
!cart.includes(p.docId) && adding.includes(p.docId)
? `pulse`
: ``
}`}
/>
<section onClick={() => addProductCart(p)}>
{cart.includes(p.docId) ? (
<img src={ICONS.shoppingFilled} />
) : (
<img src={ICONS.shoppingOutlined} />
)}
</section>
<article>
<h5>R$</h5>
{p.preco && formatCash(p.preco, true)}
</article>
</div>
))}
</Carousel>
And:
const renderArrow = ({ onClick, type, isEdge: noMoreItems }) => (
<Button
onClick={onClick}
style={{
margin: "auto 0",
borderRadius: "100px",
background: COLORS.primary,
borderColor: COLORS.primary,
width: "30px",
minWidth: "30px",
height: "30px",
minHeight: "30px",
padding: 0,
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
type="primary"
>
{type === consts.PREV ? (
<CaretLeftOutlined style={{ margin: "2px 2px 0 0" }} />
) : (
<CaretRightOutlined style={{ margin: "2px 0 0 2px" }} />
)}
</Button>
);

Categories