3

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?

Tobia
  • 17,856
  • 6
  • 74
  • 93
  • 1
    I would not worry about wasting resources. Even react new context API uses render props. – Tomasz Mularczyk Apr 04 '18 at 16:31
  • Ohh interesting! I'm curious about how `{ && ...map(item =>
  • ..
  • )}` waste resources? Doesn't it not do the map if there's a condition? – Kenneth Truong Apr 04 '18 at 16:51
  • @KennethTruong `{condition && ...}` would not waste any resources, but it would duplicate the condition expression, in the SomeWrapper prop and in the curly expression, and I'd rather avoid the duplication. – Tobia Apr 05 '18 at 08:02
  • @TomaszMularczyk you mean this? `{ctx => ...}` That looks a lot like my example #1. Would you say that it's more idiomatic than example #2? – Tobia Apr 05 '18 at 08:08
  • Nevermind, I found a lot of public discussion on the topic. https://twitter.com/kentcdodds/status/956017113994596352 http://americanexpress.io/faccs-are-an-antipattern/ etc. – Tobia Apr 05 '18 at 08:36