Reputation: 306
This should be pretty simple but keep giving me error:
Here is my array:
const section = [
{
trigger: {
title: 'Data',
icon: <Icon />,
editIcon: <Edit onClick={e => console.log(e)} />,
},
component: (
<ListOne
/>
),
},
{
trigger: {
title: 'OTHER PERSONAL DATA',
icon: <Like />,
editIcon: <Edit onClick={e => this.goToOtherPersonalData(e)} />,
},
component: (
<ListTwo
}
/>
),
},
...
]
What i wanna do is display some component from the array based on a boolean prop like this:
const section = [
propIsTrue && {
trigger: {
title: 'Data',
icon: <Icon />,
editIcon: <Edit onClick={e => console.log(e)} />,
},
component: (
<ListOne
/>
),
},
{
trigger: {
title: 'OTHER PERSONAL DATA',
icon: <Like />,
editIcon: <Edit onClick={e => this.goToOtherPersonalData(e)} />,
},
component: (
<ListTwo
}
/>
),
},
...
]
But doesn't seems to be the correct syntax and give me error:
Uncaught TypeError: Cannot read properties of undefined (reading 'onClick')
So my answer is: Which is the correct way to dynamically display components based on passed prop?
Thanks in advance
Upvotes: 0
Views: 42
Reputation: 21110
The main issue is probably propIsTrue && { object definition }
within array context. Normally you'll see this within JSX context eg.
return (
<div id="container">
{propIsTrue && (
<OnlyVisibleIfPropIsTrue />
)}
</div>
);
The reason this works in JSX is because null
, undefined
, false
, are ignored when rendering.
The above JSX is compiled into the following JavaScript.
return (
React.createElement("div", { id: "container" },
propIsTrue && (
React.createElement(OnlyVisibleIfPropIsTrue)
)
)
);
// assuming propIsTrue = false this becomes
return (
React.createElement("div", { id: "container" },
false // <- ignored by the renderer
)
);
For a standard array definition this is not the case. It does work as long as propIsTrue
is truthy.
// assuming propIsTrue = true
const people = [
propIsTrue && { name: "John Doe" },
{ name: "Jane Doe" },
];
people //=> [{ name: "John Doe" }, { name: "Jane Doe" }]
But does not "skip" the definition of the first element if propIsTrue
is falsy.
// assuming propIsTrue = false
const people = [
propIsTrue && { name: "John Doe" },
{ name: "Jane Doe" },
];
people //=> [false, { name: "Jane Doe" }]
Like you can see the people
array still has length 2, with the first element now being false
.
If you want conditional array elements you could use a normal if-statement.
// assuming propIsTrue = false
const people = [];
if (propIsTrue) people.push({ name: "John Doe" });
people.push({ name: "Jane Doe" });
people //=> [{ name: "Jane Doe" }]
The above will create an people
array with just Jane Doe and without the false
value.
Another option would be to remove the falsy values after creation.
// helper
const truthy = (item) => !!item;
// assuming propIsTrue = false
const people = [
propIsTrue && { name: "John Doe" },
{ name: "Jane Doe" },
].filter(truthy);
people //=> [{ name: "Jane Doe" }]
If you have control over the JSX that renders your array, you could use your current solution, but you'll have to skip renders for falsy values.
// assuming propIsTrue = false
const people = [
propIsTrue && { name: "John Doe" },
{ name: "Jane Doe" },
];
// ...
return (
<ul>
{people.map((person) => person && ( // <- notice the `person &&`
<li>{person.name}</li>
))}
</ul>
);
Upvotes: 1
Reputation: 556
Conditional Rendering would be the enouth solution for this case.
{propIsTrue ?
<ListOne ... />
:
<ListTwo ... />
}
If you need <Icon />
, <Edit />
or whatever compoent in your <List />
, just give it as a prop or you can implement with Component composition.
Upvotes: 0