Reputation: 91
I have an array that I am passing down to a child component, where I want all the array elements to render in a list. Not complicated, I've done this a bunch of times, except this time I'm not able to get it to format the way I want.
In my parent, I am filtering through an array of objects in my state, and returning the object that I want:
state = {
activeTab: 0,
tabs: [
{
id: 0,
icon: <AiFillShopping />,
label: 'Groceries',
content: ['grocery list', 'test']
},
{
id: 1,
icon: <FaSchool />,
label: 'School',
content: ['school list', 'test']
}
],
value: '',
}
let filteredContent = this.state.tabs.filter((tab, index) => {
if(index === this.state.activeTab) {
return tab;
}
})
console.log(filteredContent) // logs {id: 0, label: "Groceries", content: Array(2)}
Then I'm passing filteredContent to my child, and then in my child I am mapping through the prop to get the content and list the items out as a list:
<ul>
{this.props.content.map(content=> <li>{content.content}</li>)}
</ul>
But it's coming out as one list item, with the elements all in one line like this: "grocery listtest"
I'm sure I just did something stupid, but I've been staring at it for long at this point I could use another pair of eyes.
Upvotes: 2
Views: 63
Reputation: 202618
Since the content.content
is an array of renderable content, i.e. strings, it is concatenated for you by React and rendered.
You will want to flatten the nested content
arrays first, then map them to JSX.
<ul>
{content
.flatMap((content) => content.content)
.map((content) => (
<li key={content}>{content}</li>
))}
</ul>
function App() {
const content = [
{
id: 0,
// icon: <AiFillShopping />,
label: "Groceries",
content: ["grocery list", "test"]
},
{
id: 1,
// icon: <FaSchool />,
label: "School",
content: ["school list", "test"]
}
];
return (
<div className="App">
<ul>
{content
.flatMap((content) => content.content)
.map((content) => (
<li key={content}>{content}</li>
))}
</ul>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<App />,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />
If you happen to just want a comma separated content list they join the content array.
<ul>
{content.map((content) => (
<li key={content}>{content.content.join(", ")}</li>
))}
</ul>
Array.prototype.filter
callback returns a boolean, not the element you want in the result array. Your filter code only works by "accident" since returning a tab
is coerced to a truthy value, and when the index doesn't match you are implicitly returning undefined which is a falsey value. You should just return the result of the condition.
const filteredContent = this.state.tabs.filter(
(tab, index) => index === this.state.activeTab
);
Since you are filtering the tabs array and (presumably) rendering them then you might not want to use the array index as it isn't intrinsically related to any tab. Using the tab.id
and storing an active tab id would a better choice since the id
isn't related to any relative position in the array it'll always be the same per tab object.
Upvotes: 4