Conditionally adding a wrapping element
Answered
English Lop posted this in #help-forum
English LopOP
Hello all,
I'm a relatively new React developer. I have a design that has two rows of three elements. To render this, I'm using the map method on an array to output values into the elements. Every three elements, I'm creating a new row. I ran into an issue where I couldn't short-circuit a wrapping element. I.e.
I was able to find something that worked using AI, but I'd like someone more experienced to let me know if this is a good way to accomplish my goal:
I'm a relatively new React developer. I have a design that has two rows of three elements. To render this, I'm using the map method on an array to output values into the elements. Every three elements, I'm creating a new row. I ran into an issue where I couldn't short-circuit a wrapping element. I.e.
{index % 3 === 0 && <Row>} .... {index % 3 === 0 && </Row>}
I was able to find something that worked using AI, but I'd like someone more experienced to let me know if this is a good way to accomplish my goal:
{gridTileData
.reduce<React.JSX.Element[][]>((rows, tile, i) => {
if (i % 3 === 0) {
// Start a new row every 3 items
rows.push([]);
}
// Push current tile into the latest row array
rows[rows.length - 1].push(
<Column
key={i}
radius="l"
border="neutral-medium"
borderStyle="solid"
className="justify-space-between"
fillWidth
padding="s"
ref={(element) => {
if (element) gridTileRefs.current[i] = element;
}}
>
<Row gap="s" className="service-tile">
<Column className="service-tile-img">
<img src={tile.image} alt={tile.alt} />
</Column>
<Column gap="s">
<Heading onBackground="info-strong" className="banner-text">
{tile.title}
</Heading>
<Text>{tile.description}</Text>
</Column>
</Row>
<Column className="service-tile-button">
<Button className="banner-button" size="l">
Learn More
</Button>
</Column>
</Column>,
);
return rows;
}, [])
.map((row, index) => (
<Row key={`row-${index}`} className="service-tile-row" gap="s">
{row}
</Row>
))}
Answered by English Lop
This is the code from the screenshot
{gridTileData.map((tile, i) => (
<Column
key={i}
radius="l"
border="neutral-medium"
borderStyle="solid"
className="justify-space-between"
padding="s"
ref={(element) => {
if (element) gridTileRefs.current[i] = element;
}}
>
<Row gap="s" className="service-tile">
<Column className="service-tile-img">
<img src={tile.image} alt={tile.alt} />
</Column>
<Column gap="s">
<Heading onBackground="info-strong" className="banner-text">
{tile.title}
</Heading>
<Text>{tile.description}</Text>
</Column>
</Row>
<Column className="service-tile-button">
<Button className="banner-button" size="l">
Learn More
</Button>
</Column>
</Column>
))}
14 Replies
@Yi Lon Ma try something like this
English LopOP
Not sure what you mean. Could you clarify a bit?
@English Lop Not sure what you mean. Could you clarify a bit?
look at the
Comp
variablehow it renders a
Slot
when asChild
prop is true, else it renders a button
@Yi Lon Ma how it renders a `Slot` when `asChild` prop is true, else it renders a `button`
English LopOP
I see. Having trouble putting it together. I see you're setting this variable before the return. My conditional - the index(i) of the map function inside the return is out of scope. And I don't think I can declare a variable inside of the return?
I mean I guess I could pull logic out and put it above the return into a for each and then output the results in the render.
I did not know that I could set variable component names though. Thank you for teaching me this!
you can
{
gridTileData.map((tile,i)=>{
const RowTest = something;//declare here
return <RowTest/>
})
}
English LopOP
Ah you're right. This syntax difference. In the image I posted, I'm going straight to the return correct? In yours I'm in an anon function?
English LopOP
This is the code from the screenshot
{gridTileData.map((tile, i) => (
<Column
key={i}
radius="l"
border="neutral-medium"
borderStyle="solid"
className="justify-space-between"
padding="s"
ref={(element) => {
if (element) gridTileRefs.current[i] = element;
}}
>
<Row gap="s" className="service-tile">
<Column className="service-tile-img">
<img src={tile.image} alt={tile.alt} />
</Column>
<Column gap="s">
<Heading onBackground="info-strong" className="banner-text">
{tile.title}
</Heading>
<Text>{tile.description}</Text>
</Column>
</Row>
<Column className="service-tile-button">
<Button className="banner-button" size="l">
Learn More
</Button>
</Column>
</Column>
))}
Answer
with arrow functions, you can straight up return the component, which you are doing.
but you can also use a block scope, where you can put any logic and return the component once done
but you can also use a block scope, where you can put any logic and return the component once done
it is just like declaring a component but inline or you can pass props to your returning component and have the logic there
English LopOP
Gotcha! Thank you.
English LopOP
I played around with the earlier suggestion and I still ran into issues still with wrapping my inside elements in the RowTest. Some internet searching makes me think that maybe I can do something like:
Then
With the result being RowTest is only rendered if the ternary statement is true. But I believe since I am working in TypeScript I get errors for RowTest not being a defined Component. I tried throwing in div at the other end of the expression but that didn't help things.
Doing more digging I did find that I could create a dedicated component for wrapping things and also using a multi-dimensional array for my data. Tried out the multi-d array and it did make my code a lot cleaner:
const RowTest = i % 3 === 0 ? Row : ''
Then
<RowTest> ....[my elements]...</RowTest>
With the result being RowTest is only rendered if the ternary statement is true. But I believe since I am working in TypeScript I get errors for RowTest not being a defined Component. I tried throwing in div at the other end of the expression but that didn't help things.
Doing more digging I did find that I could create a dedicated component for wrapping things and also using a multi-dimensional array for my data. Tried out the multi-d array and it did make my code a lot cleaner:
{gridTileData.map((gridTile, index) => (
<Row key={index} className="GridTest">
{gridTile.map((tile, tileIndex) => (...))}
</Row>)}