How to pass conditional children in React / Preact (aka. <If> component)

3.4k Views Asked by At

Sometimes I need to create a wrapper element that will show its children (or not) according to its own logic, optionally wrapping them in its own choice of elements:

<SomeWrapper some={condition}>
  Hello
</SomeWrapper>

This works because the children ("Hello") are static. But what if the children are to be computed dynamically and may only be well-defined when the condition holds?

<SomeWrapper some={condition}>
  <ul>
    {this.may.not.exist.unless.condition.map(item => 
      <li key={item.id}>{item.text}</li>
    )}
  </ul>
</SomeWrapper>

Here, if the condition is false and the wrapper element does not make use of its children, they will still be created and passed down the tree, wasting resources and possibly throwing an error in the process.

One solution (probably the best?) is to wrap the contents in their own component:

<SomeWrapper some={condition}>
  <InnerContent/>
</SomeWrapper>

This works because (AFAIK, correct me if I'm wrong) InnerContent's constructor and render will not be called unless SomeWrapper actually decides to make use of its children prop.

But what if I don't want to create a component for 3 lines of code?

I have seen two options in the wild, none of which are particularly appealing:

  1. passing a thunk as the only child:

    <SomeWrapper some={condition}>{() =>
      <ul>  
        {this.may.not.exist.unless.condition.map(item => 
          <li key={item.id}>{item.text}</li>
        )}
      </ul>
    }</SomeWrapper>
    
  2. passing a thunk as a prop:

    <SomeWrapper some={condition} render={() =>
      <ul>  
        {this.may.not.exist.unless.condition.map(item => 
          <li key={item.id}>{item.text}</li>
        )}
      </ul>
    }/>
    

I don't like them because the lambda adds visual noise to the code, not to mention wasting resources, being re-created at every render() execution (AFAIK.)

Is there any other solution I'm not seeing? Should I always go with the InnerContent element?

2

There are 2 best solutions below

0
On

you can simply do the following

<Container> {yourCondition === true && <ConditionalChildElement/>} </Container>

0
On

It seems that render props are a thing, in fact there's a React page explaining their usage and best practices:

https://reactjs.org/docs/render-props.html