Reputation: 8587
How do you deal with conditional within a loop in reactjsx where I want to create a <div>
and closing </div>
every so often so I can create rows
Right now I have something like this:
renderContent = () => {
const { classes, product } = this.props;
if (!product) {
return (
<>
<Content />
<Space width="10px" />
<Content />
<Space width="10px" />
<Content />
<Space width="10px" />
<Content />
<Space width="10px" />
<Content />
</>
);
}
const sizeLength = product.length;
const content = [];
for (let i = 0; i < sizeLength; i++) {
if (i === 0 || i % 5) content.push(<div>)
content.push(
<Fragment key={`${i}`}>
<Content />
<Space width="10px" />
</Fragment>,
);
if (i === 4 || i % 4 ) content.push(</div>)
}
return content;
}
So the code renders 5 <Content />
if product is null. This is all in one row.
What I'm trying to accomplish is to have something like this:
<div>
<Content />
<Space width="10px" />
<Content />
<Space width="10px" />
<Content />
<Space width="10px" />
<Content />
<Space width="10px" />
<Content />
</div>
A wrapping every 5 loop, so if there's a 6th, it would be someting like this:
<div>
<Content />
<Space width="10px" />
<Content />
<Space width="10px" />
<Content />
<Space width="10px" />
<Content />
<Space width="10px" />
<Content />
</div>
<div>
<Content />
</div>
But the code I have is breaking and doesn't work. Is there something I'm missing? I'm not sure if the content.push is correct, perhaps there's a better way of dealing with this?
Upvotes: 0
Views: 1303
Reputation: 328
In React JSX, you can't have standalone tags like <div>
or </div>
. Tags define React elements and must always be self-closing or come in pairs with 0 or more elements in between. For example:
// OK - self closing tag. Creates a React div element with no children
const a = <div />;
// OK - Same as above
const b = <div></div>;
// OK - Creates div element with one child, a br element
const c = <div>
<br />
</div>;
// OK - Creates a div element and executes JS in the braces
// to create two img children
const d = <div>{
[<img />, <img />]
}</div>;
// Not OK - Everything between the div tags is treated as text (i.e. `; const e = `)
const d = <div>;
const e = </div>;
As you can see in your code, you are pushing standalone tags into your array which is not OK. Instead, use nested loops and nest the element arrays:
const content = [];
const productLength = product.length || 5;
for (let i = 0; i < productLength; i += 5) {
const innerContent = [];
const innerLength = Math.min(5, (productLength - i));
for (let j = 0; j < innerLength; j++) {
innerContent.push(
<Fragment key={i+j}>
<Content />
{
// Don't render the last space. React ignores null children
j !== innerLength - 1 ? <Space width="10px" /> : null
}
</Fragment>
);
}
content.push(<div>{innerContent}</div>);
}
return content;
It's generally a bad idea to use indices as element keys, so prefer to use an ID that you have on your product object, e.g. key={product[i+j].id}
Upvotes: 1
Reputation: 20785
Put the items in a buffer array, then flush them into a div every 5 items and add that div to the rendering array
const content = [];
let buffer = [];
for (let i = 0; i < sizeLength; i++) {
buffer.push(
<Fragment key={`${i}`}>
<Content />
<Space width="10px" />
</Fragment>,
);
if (i % 5) {
content.push(<div>{buffer}</div>);
buffer = [];
}
}
// one last push if there are left over items
if (buffer.length > 0) content.push(<div>{buffer}</div>);
return content;
Upvotes: 1