So I have a react component set up to map through all the items in my array to display them on the page. I'm importing my component onto my homepage and passing the object as a prop from the imported component. However, when I load the page, only one item from the object is being rendered. I'm not entirely sure if I'm passing my object correctly. Any help would be appreciated! Code is below.
This is my Modal component. I'm mapping through the listGroupArray that has a spread operator with my data that is being passed from the home page.
export default function ModalButton({ setData, title, arrayData, dataTitle }) {
const [show, setShow] = useState(false);
const [button, setButton] = useState("Choose...")
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const listGroupArray = [{...arrayData}]
const changeButton = e => setButton(e)
return (
<>
<h5 className="inputFont text-center">{title}</h5>
<Button style={{ backgroundColor: "black", opacity: "1", color: "white", borderColor: "red" }} variant="primary" className="w-100 mb-4 inputFont" onClick={handleShow}>
{button}
</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton className="modal-bg inputFont">
{dataTitle}
</Modal.Header>
<Modal.Body className="modal-bg">
<ListGroup>
{listGroupArray.map(item => (
<ListGroup.Item key={item.id} className="modal-bg">
<Button
style={{
backgroundColor: "black",
opacity: ".8",
color: "white",
borderColor: "red",
}}
className="inputFont w-100"
name={item.name}
value={item.value}
onClick={(e) => {
setData(item.value);
changeButton(item.name);
handleClose();
}}
>
{item.name}
</Button>
</ListGroup.Item>
))}
</ListGroup>
</Modal.Body>
</Modal>
</>
);
}
This is my homepage where I'm passing the array data as an object. I'm pretty sure this is where I'm going wrong. When I load the page, the component should render all the data in the object, however it's only rendering the last data, Classics.
<Modal title="Genre"
dataTitle="Pick A Genre"
setData={setGenrelist}
arrayData={
{
id: 1,
name: "Action and Adventure",
value: "10673,10702,11804,11828,1192487,1365,1568,2125,2653,43040,43048,4344,46576,7442,75418,76501,77232,788212,801362,899,9584"
},
{
id: 2,
name: "Musicals",
value: "13335,13573,32392,52852,55774,59433,84488,88635"
},
{
id: 3,
name: "Sci-Fi",
value: "1492,108533,11014,1372,1568,1694,2595,2729,3327,3916,47147,4734,49110,50232,52780,52849,5903,6000,6926,852491"
},
{
id: 4,
name: "Fantasy",
value: "9744"
},
{ id: 5,
name: "Thrillers",
value: "10306,10499,10504,10719,11014,11140,1138506,1321,1774,3269,43048,46588,5505,58798,65558,6867,75390,78507,799,852488,8933,98911,9147,972"
},
{
id: 6,
name: "Anime",
value: "10695,11146,2653,2729,3063,413820,452,6721,9302,7424"
},
{
id: 7,
name: "Children and Family",
value: "10056,27480,27950,28034,28083,28233,48586,5455,561,6218,6796,6962,78120,89513,783"
},
{
id: 8,
name: "Comedies",
value: "1009,10256,10375,105,10778,11559,11755,1208951,1333288,1402,1747,17648,2030,2700,31694,3300,34157,3519,3996,4058,4195,43040,4426,4906,52104,52140,52847,5286,5475,5610,56174,58905,59169,61132,61330,6197,63092,63115,6548,711366,7120,72407,7539,77599,77907,78163,78655,79871,7992,852492,869,89585,9302,9434,9702,9736"
},
{
id: 9,
name: "Documentaries",
value: "10005,10105,10599,1159,15456,180,2595,2616,2760,28269,3652,3675,4006,4720,48768,49110,49547,50232,5161,5349,55087,56178,58710,60026,6839,7018,72384,77245,852494,90361,9875"
},
{
id: 10,
name: "Dramas",
value: "11,11075,11714,1208954,1255,12995,13158,2150,25955,26009,2696,2748,2757,2893,29809,3179,31901,34204,3653,3682,384,3916,3947,4282,4425,452,4961,500,5012,52148,52904,56169,58755,58796,59064,6206,62235,6616,6763,68699,6889,711367,71591,71591,72354,7243,7539,75459,76507,78628,852493,89804,9299,9847,9873,5763"
},
{
id: 11,
name: "Sports",
value: "180,25788,4370,5286,7243,9327"
},
{
id: 12,
name: "Horror",
value: "10695,10944,1694,42023,45028,48303,61546,75405,75804,75930,8195,83059,8711,89585"
},
{
id: 13,
name: "Romance",
value: "29281,36103,502675"
},
{
id: 14,
name: "Classics",
value: "10032,11093,13158,29809,2994,31273,31574,31694,32392,46553,46560,46576,46588,47147,47465,48303,48586,48744,76186"
}
}
/>
screenshot of the homepage
This image shows the component only rendering one data item which is Classics. Any advice on how to get all data rendered would be greatly appreciated! Thanks!
The error is in how you referenced the arrayData and the problematic curly brackets you used on an array. In your JSX code, you have a syntax error, you are supposed to enclose arrays in curly brackets, or better still just separate them to their own variable.
Your JSX should then look something like this:
function JSX(props) {
const arrayData = [
{
id: 1,
name: "Action and Adventure",
value:
"10673,10702,11804,11828,1192487,1365,1568,2125,2653,43040,43048,4344,46576,7442,75418,76501,77232,788212,801362,899,9584",
},
{
id: 2,
name: "Musicals",
value: "13335,13573,32392,52852,55774,59433,84488,88635",
},
{
id: 3,
name: "Sci-Fi",
value:
"1492,108533,11014,1372,1568,1694,2595,2729,3327,3916,47147,4734,49110,50232,52780,52849,5903,6000,6926,852491",
},
{
id: 4,
name: "Fantasy",
value: "9744",
},
{
id: 5,
name: "Thrillers",
value:
"10306,10499,10504,10719,11014,11140,1138506,1321,1774,3269,43048,46588,5505,58798,65558,6867,75390,78507,799,852488,8933,98911,9147,972",
},
{
id: 6,
name: "Anime",
value: "10695,11146,2653,2729,3063,413820,452,6721,9302,7424",
},
{
id: 7,
name: "Children and Family",
value:
"10056,27480,27950,28034,28083,28233,48586,5455,561,6218,6796,6962,78120,89513,783",
},
{
id: 8,
name: "Comedies",
value:
"1009,10256,10375,105,10778,11559,11755,1208951,1333288,1402,1747,17648,2030,2700,31694,3300,34157,3519,3996,4058,4195,43040,4426,4906,52104,52140,52847,5286,5475,5610,56174,58905,59169,61132,61330,6197,63092,63115,6548,711366,7120,72407,7539,77599,77907,78163,78655,79871,7992,852492,869,89585,9302,9434,9702,9736",
},
{
id: 9,
name: "Documentaries",
value:
"10005,10105,10599,1159,15456,180,2595,2616,2760,28269,3652,3675,4006,4720,48768,49110,49547,50232,5161,5349,55087,56178,58710,60026,6839,7018,72384,77245,852494,90361,9875",
},
{
id: 10,
name: "Dramas",
value:
"11,11075,11714,1208954,1255,12995,13158,2150,25955,26009,2696,2748,2757,2893,29809,3179,31901,34204,3653,3682,384,3916,3947,4282,4425,452,4961,500,5012,52148,52904,56169,58755,58796,59064,6206,62235,6616,6763,68699,6889,711367,71591,71591,72354,7243,7539,75459,76507,78628,852493,89804,9299,9847,9873,5763",
},
{
id: 11,
name: "Sports",
value: "180,25788,4370,5286,7243,9327",
},
{
id: 12,
name: "Horror",
value:
"10695,10944,1694,42023,45028,48303,61546,75405,75804,75930,8195,83059,8711,89585",
},
{
id: 13,
name: "Romance",
value: "29281,36103,502675",
},
{
id: 14,
name: "Classics",
value:
"10032,11093,13158,29809,2994,31273,31574,31694,32392,46553,46560,46576,46588,47147,47465,48303,48586,48744,76186",
},
];
return (
<Modal
title="Genre"
dataTitle="Pick A Genre"
setData={(data) => console.log(data)}
arrayData={arrayData}
/>
);
}
The next bug you had was in using the spread operator. While arrays are technically objects in JavaScript they can't be spread with curly braces. This [{...arrayData}] is syntactically incorrect. Instead it should be [...arrayData]. With these in place, your code should run correctly.
I made a sandbox of your code in a working state for reference, check it out here:
https://codesandbox.io/s/young-snowflake-efqwk?file=/src/ModalButton.js:877-883
Related
I want to render a Card Item for each dropdown item in my react-multi-select component.
I know there is a way to change the label using the item renderer as shown in Manipulate React Render Properties but is there a way to render a more complicated UI that consists of images and divs?
This is my current code:
<Multiselect
defaultMenuIsOpen
placeholder=""
{...field}
inputRef={ref}
displayValue="name"
onSelect={(selected, item) => {
setValue('test', selected)
}}
onRemove={(selected, item) => {
setValue('test', selected)
}}
options={[
{ value: 'chocolate', name: 'Chocolate', id: 1 },
{ value: 'strawberry', name: 'Strawberry', id: 2 },
{ value: 'vanilla', name: 'Vanilla', id: 3 },
]}
/>
The code above gives me a UI of
The data that I want to add in is as below:
const data = [
{
id: 1,
title: 'Test1',
supply: 1,
owners: 5,
},
{
id: 2,
title: 'Test2',
supply: 10,
owners: 50,
},
{
id: 3,
title: 'Test3',
supply: 100,
owners: 500.
},
]
I can't get rid of the classic React "Each child in the list should have a unique key".
I'm rendering a big array, with items that can have sub items.
To prevent all of this, i created a data structure with unique id for every item.
This is my data:
export const assetListConfig: RolesListItem[] = [
{
id: 1,
title: t('Asset creation'),
name: 'Asset-Create',
type: 'switch',
},
{
id: 2,
title: t('Edit asset info'),
name: 'Asset-Edit',
type: 'switch',
},
{
id: 3,
title: t('Archive'),
name: 'Asset-Archive',
type: 'switch',
},
{
id: 4,
title: t('Delete'),
name: 'Asset-Delete',
type: 'switch',
subLists: [
{
subListWatch: 'Asset-Delete',
items: [
{
id: 5,
title: t('Bulk deletion'),
name: 'Asset-Delete-Bulk',
type: 'switch',
},
],
},
],
},
{
id: 6,
title: t('Resources'),
name: 'Asset-Manage-Resources',
type: 'switch',
},
{
id: 7,
title: t('Enable public'),
name: 'Asset-Set-Public',
type: 'switch',
},
{
id: 8,
title: t('Enable redirect'),
name: 'Asset-Set-Redirect',
type: 'switch',
},
{
id: 9,
title: t('Enable tickets'),
name: 'Asset-Enable-Tickets',
type: 'switch',
},
{
id: 10,
title: t('Edit hierarchy'),
name: 'Asset-Edit-Hierarchy',
type: 'switch',
itemWatch: ['Asset-Edit'],
},
{
id: 11,
title: t('Locations'),
name: 'Asset-Location',
type: 'switch',
},
{
id: 12,
title: t('Manage asset tags'),
name: 'Asset-Manage-Tags',
type: 'switch',
},
{
id: 13,
title: t('Logbook'),
name: 'Asset-Logbook-Read',
type: 'switch',
subLists: [
{
subListWatch: 'Asset-Logbook-Read',
items: [
{
id: 14,
title: t('Comment'),
name: 'Asset-Logbook-Comment',
type: 'switch',
},
{
id: 15,
title: t('Allow export'),
name: 'Asset-Logbook-Export',
type: 'switch',
},
],
},
],
},
{
id: 16,
title: t('Components'),
name: 'Component-Read',
type: 'switch',
subLists: [
{
subListWatch: 'Component-Read',
items: [
{
id: 17,
title: t('Instances'),
name: 'Component-Instance',
type: 'radio',
radioOptions: [
{ label: t('Manage'), value: 'Component-Instance-Manage' },
{
label: t('Delete'),
value: 'Component-Instance-Delete',
inputWatch: 'Component-Instance-Manage',
},
],
},
{
id: 18,
title: t('Models'),
name: 'Component-Model',
type: 'radio',
radioOptions: [
{
label: t('Manage'),
value: 'Component-Model-Manage',
inputWatch: 'Component-Instance-Manage',
},
{
label: t('Delete'),
value: 'Component-Model-Delete',
inputWatch: 'Component-Model-Manage',
},
],
},
],
},
],
},
{
id: 19,
title: t('Workshifts'),
name: 'Asset-Running-Read',
type: 'switch',
subLists: [
{
subListWatch: 'Asset-Running-Read',
items: [
{
id: 20,
title: t('Manage status'),
name: 'Asset-Running-Status-Manage',
type: 'switch',
},
{
id: 21,
title: t('Manage expectations'),
name: 'Asset-Running-Expectation-Manage',
type: 'switch',
},
{
id: 22,
title: t('Manage schedules'),
name: 'Asset-Running-Schedule-Manage',
type: 'switch',
},
],
},
],
},
];
I render lists with .map also for radiogroup options and item sublists.
This is the component in which I get the error:
import shortid from 'shortid'
export const UserRolesList: React.FC<ListItemProps> = ({
listConfig,
userData,
isSubList = false,
}) => {
handleChange ecc ecc...
return (
<RolesListWrapper isSubList={isSubList}>
<ul
className="list--unbulleted display--ib full-width"
>
/* FIRST LIST RENDERING */
**{listConfig.map((listItem, index) => {
const {
name,
title,
type,
radioOptions = [],
selectOptions = [],
id: skillId,
subLists,
} = listItem;**
const visibleOptions = renderVisibleOptions(radioOptions);
return (
<>
{isListItemVisible(listItem) ? (
**<li
className={`pad pad-double--sides shadow--list-item ${
index !== listConfig.length - 1 ? 'push--bottom' : ''
}`}
key={skillId}
style={
isSubList
? {
padding: '0',
boxShadow: 'none',
}
: {
width: 'auto',
minWidth: '350px',
}
}
>**
<div className="full-width flex--space-items">
<div
className="pad flex--align-center"
style={{
paddingLeft: isSubList ? 'var(--base-space)' : '',
flexBasis: '160px',
}}
>
{title}
</div>
{type === 'switch' ? (
<div className="pad flex--align-center">
<Switch
onColor="#21b151"
offColor="#e02f2f"
checked={findActiveSkill(name)}
onChange={() => handleSkillChange(name)}
disabled={isAddingSkill || isRemovingSkill}
/>
</div>
) : null}
{type === 'radio' ? (
**<div
className="flex-item--fill"
style={{
display: 'grid',
gridTemplateColumns: 'repeat(3,1fr)',
}}
>
/* SECOND LIST RENDERING */
{visibleOptions.map((opt) => (
<div
className="flex--align-center"
style={{
gridColumn: opt.value.includes('Delete')
? '3'
: undefined,
}}
key={shortid.generate()}
>**
<input
type="checkbox"
className="qtrack-form-input__selection"
value={opt.value}
id={opt.value}
checked={findActiveSkill(opt.value)}
onChange={(e) =>
handleSkillChange(e.target.value)
}
disabled={isAddingSkill || isRemovingSkill}
/>
<label
style={{ margin: '5px 0' }}
htmlFor={opt.value}
>
{opt.label}
</label>
</div>
))}
</div>
) : null}
{type === 'select' ? (
<div style={{ margin: '0', minWidth: '220px' }}>
<SimpleSelect
selectOptions={selectOptions}
value={selectOptions.find((opt) =>
skills.includes(opt.value),
)}
isMulti={false}
isClearable
errors={undefined}
onChange={(e: string) => handleSelectChange(e, name)}
name="roles-select"
isDisabled={isAddingSkill || isRemovingSkill}
/>
</div>
) : null}
</div>
{subLists ? (
<SubListHandler subLists={subLists} userData={userData} />
) : null}
</li>
) : null}
</>
);
})}
</ul>
</RolesListWrapper>
);
};
And this is the component in which I render the sublists:
export const SubListHandler: React.FC<{
subLists: RolesList[];
userData: UserData;
}> = ({ subLists, userData }) => (
<>
/* THIRD AND LAST LIST RENDER */
{subLists.map((list) => {
const { subListWatch, items: subItems } = list;
const isSubListVisible = userData.skills.includes(subListWatch);
**const subListId = shortid.generate();**
return (
<div key={subListId}>
{isSubListVisible ? (
**<div>**
{list.title ? (
<div className="txt--bold pad--left push--top">
{list.title}
</div>
) : null}
<div className="push--ends pad--left">
<UserRolesList
listConfig={subItems}
userData={userData}
isSubList
/>
</div>
</div>
) : null}
</>
);
})}
</>
);
As you can see, I'm using unique IDs for all the three map methods in my component.
This is confirmed when I log the IDs I used in the console. They're all different.
I don't know what I'm doing wrong or if I'm messing something with the conditional rendering or nested components
I tried to highlight when the list rendering happens in the code.
This seems pretty straighforward to me as I have unique ids for every list child, but it always throws me an error.
Let me know if you find something wrong with my code.
Thanks a lot!
You need to use the unique id in the 'key' prop of the list items. ie. -
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
I want to change border or outline color when I click for one item not all list items
but when I click on any items all list changed
I'm trying to change one item color not all...so when I change state it changed for all not for item I clicked
const [color,setColor]=useState('');
type Values = {
id: number;
title: string;
image: string;
color:string;
};
const myList2:Array<Values> =[
{
id: 1,
title: 'Suitcase',
image: suitcase,
color:'blue',
},
{
id: 2,
title: 'Briefcase',
image: briefcase,
color:'aqua',
},{
id: 3,
title: 'Handbage',
image: handbage,
color:'red',
},
{
id: 4,
title: 'Multy',
image: multy,
color:'green',
},
{
id: 5,
title: 'Backpack',
image: backpack,
color:'gray',
},
{
id: 6,
title: 'Family',
image: family,
color:'orange',
},
]
const listImage=myList2.map((item,i) => {
return <span key={i}>
<img key={item.id} style={{borderColor:color}} onClick={()=>setColor(item.color)} src={item.image} alt={item.title} />
</span>
})
Currently you are setting only color name in the state variable but not specify that for which index this color will apply
So simply store only index to the state variable on which user click by adding onClick={()=>setActive(i)} in the image tag
and finally check the condition in style by adding style={{borderColor: active == i ? item.color : 'transparent', borderWidth: 3}} in the image tag
const [active,setActive]=useState(-1);
type Values = {
id: number;
title: string;
image: string;
color:string;
};
const myList2:Array<Values> =[
{
id: 1,
title: 'Suitcase',
image: suitcase,
color:'blue',
},
{
id: 2,
title: 'Briefcase',
image: briefcase,
color:'aqua',
},{
id: 3,
title: 'Handbage',
image: handbage,
color:'red',
},
{
id: 4,
title: 'Multy',
image: multy,
color:'green',
},
{
id: 5,
title: 'Backpack',
image: backpack,
color:'gray',
},
{
id: 6,
title: 'Family',
image: family,
color:'orange',
},
]
const listImage=myList2.map((item,i) => {
return <span key={i}>
<img key={item.id} style={{borderColor: active == i ? item.color : 'transparent', borderWidth: 3}} onClick={()=>setActive(i)} src={item.image} alt={item.title} />
</span>
})
You can use the id for that. So instead of only the color name, keep two pieces of information as an object inside the state variable => id, color.
Like this:
const [selectedList, setSelectedList] = useState({});
const listImage=myList2.map((item,i) => {
return <span key={i}>
<img key={item.id}
style={item.id === selectedList.id ? { borderColor: selectedList.color } : {}}
onClick={() => setSelectedList({id: item.id, color: item.color})}
src={item.image} alt={item.title} />
</span>
})
I created a "skillProg" state using useState and want to change it using the "setSkillProg" inside a map loop. But I get a unexpected token error. Here is the code:
const Skills = () => {
const skills = [
{ bgcolor: "#0bd30e", completed: 100, name: "HTML" },
{ bgcolor: "#0bd30e", completed: 100, name: "CSS" },
{ bgcolor: "#0bd30e", completed: 90, name: "JavaScript" },
{ bgcolor: "#0bd30e", completed: 90, name: "React" },
{ bgcolor: "#0bd30e", completed: 90, name: "Express" },
{ bgcolor: "#0bd30e", completed: 90, name: "MongoDB" },
{ bgcolor: "#0bd30e", completed: 90, name: "Firebase" },
{ bgcolor: "#0bd30e", completed: 90, name: "PostGreSQL" },
{ bgcolor: "#0bd30e", completed: 90, name: "Git" },
{ bgcolor: "#0bd30e", completed: 90, name: "JavaScript" },
];
const [skillProg, setSkillProg] = useState(0);
return (
<div className="skills">
{skills.map((skill, idx) => (
{setSkillProg({skill.completed})}
<ProgressBar
key={idx}
bgcolor={skill.bgcolor}
completed={skillProg}
name={skill.name}
className="progress_bar"
/>
))}
</div>
);
};
export default Skills;
When working with JSX elements in ReactJS, you typically return these values when working in map() (as well as when working within render()). Try this...
return(
<ProgressBar
key={idx}
bgcolor={skill.bgcolor}
completed={skillProg}
name={skill.name}
className="progress_bar"
/>
);
Take a look at the default syntax from MDN Web Docs, you'll see the return() there, too...
let newArray = arr.map(callback(currentValue[, index[, array]]) {
// return element for newArray, after executing something
}[, thisArg]);
Even though it's not JSX or ReactJS, it's still returning something.
P.S. I have asked for further debugging details, I will update my answer as needed when I get these.
I am trying to map through a nested Array using JSX.
Here is the Array:
this.topics = [
{
id: 1,
name: 'first',
headings : [
{
id: 1,
url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
name: 'Sintel movie'
},
{
id: 2,
url: 'https://media.w3.org/2010/05/bunny/movie.mp4',
name: 'Bunny Movie'
},
{
id: 3,
url: 'https://techslides.com/demos/sample-videos/small.mp4',
name: 'Test page'
}
]
},
{
id: 2,
name: 'second',
headings : [
{
id: 1,
url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
name: 'Siddntel movie'
},
{
id: 2,
url: 'https://media.w3.org/2010/05/bunny/movie.mp4',
name: 'Bunnddy Movie'
},
{
id: 3,
url: 'https://techslides.com/demos/sample-videos/small.mp4',
name: 'Test ddpage'
}
]
}
];
And the JSX code that I have come up with so far:
renderSidenav(){
return(
<Nav>
{this.topics.map(topic =>
<Dropdown eventKey="3" title="s" icon={<Icon icon="magic" />}>
{this.topics[topic].headings.map(heading =>
<div onClick = {() => this.handleSelect(heading.id)} key={heading.id}>
<Dropdown.Item style={{backgroundColor: '#E9F4E4'}} icon={<Icon icon="dashboard"/>}>
<div>{heading.name}</div>
</Dropdown.Item>
<Dropdown.Item divider style={{backgroundColor: 'white', height: '2px'}}/>
</div>
)}
</Dropdown>
)}
</Nav>
)
}
And this is the error I get:
TypeError: Cannot read property 'headings' of undefined
What am I doing wrong?
Instead of this.topics[topic].headings.map(...) use topic.headings.map(...)
This is because .map() returns an object, and trying to use it as an index via topics[topic] will give you undefined.
Just do topic.headings.map instead of this.topics[topic].headings.map.
Map function returns the item, not the position/index in the array, therefore you can call it directly from topic.