Next.js Discord

Discord Forum

Conditionally adding a wrapping element

Answered
English Lop posted this in #help-forum
Open in Discord
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. {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>
          ))} 
View full answer

14 Replies

try something like this
@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 variable
how 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
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:

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>)}