React.Fragment, the only child
Tags: javascript, react, fragments
Published: 2019-04-21 18:08:16

---
title: React.Fragment, the only child
published: true
description: This is a short post about some issues I had while building a wizard component.
tags: JavaScript, Reactjs, Fragments
---

This is a short post about some issues I had while building a wizard component in ReactJS.
- You can't reference a "falsy" child while using `React.cloneElement`.
- `React.Fragment` returns a single child.

At the beginning my wizard instance looked something like this:
```
<Wizard>
  <Step1 />
  <Step2 />
  <Step3 />
  <Step4 />
  <Step5 />
</Wizard>
```
Behind the scenes, the component will only render the current step.
```
render () {
  const { children } = this.props
  const { activeStep } = this.state
  const extraProps = {...} // Some extra info I need on each step.
  return (
    …
    {React.cloneElement(children[activeStep], extraProps)}
    …
  )
}
```
Based on some business rules, I wanted to hide/show some steps, so my wizard instance will look something like this:
```
renderStep2 () {
  if (conditionForStep2) {
    return <Step2 />
  }
}
render () {
  return ( 
    <Wizard>
      <Step1 />
      {this.renderStep2()}
      <Step3 />
      {conditionForStep4 && <Step4 />}
      <Step5 />
    </Wizard>
  )
}
```
Those expressions evaluate to undefined for `Step2` and false for `Step4`, and any of those values can be used as a valid child when doing `React.cloneElement(children[activeStep], extraProps)` where `activeStep` is the index of `Step2` or `Step4`, React will complain 😩 and also my index will be wrong.
![React error. Element type is invalid](https://thepracticaldev.s3.amazonaws.com/i/rolxf46uxm08o11snwpp.png)

Instead of using children directly, I created a function that returns only the "truthy" steps:
```
const getChildren = children => children.filter(child => !!child)
And change my Wizard render function to something like this:
render () {
 const { children } = this.props
 const { activeStep } = this.state
 const filteredChildren = getChildren(children)
 return (
   …
   {React.cloneElement(filteredChildren[activeStep], extraProps)}
   …
 )
}
```
The first problem solved 🎉

I got to the point where I wanted to group some steps in order to simplify my logic. Let's say for example that I need to use the same condition for rendering `Step3`, `Step4` and `Step5`, so I grouped them into a `React.Fragment`.
```
renderMoreSteps () {
  if (condition) {
    return (
      <Fragment>
        <Step3 />
        <Step4 />
        <Step5 />
      </Fragment>
    )
  }
}
```
And my Wizard instance:
```
<Wizard>
  <Step1 />
  <Step2 />
  {this.renderMoreSteps()}
</Wizard>
```
The problem: Even though Fragment is not represented as DOM elements, it returns a single child instead of individual child components.
The solution: flatten children.
```
import { isFragment } from 'react-is'
const flattenChildren = children => {
  const result = []
  children.map(child => {
    if (isFragment(child)) {
      result.push(…flattenChildren(child.props.children))
    } else {
      result.push(child)
    }
  })
  return result
}
```
Updated getChildren function:
```
const getChildren = children => flattenChildren(children).filter(child => !!child && !isEmpty(child))
```
For simplicity, I used [react-is](https://www.npmjs.com/package/react-is), but the implementation is straight forward:
```
function isFragment (object) {
  return typeOf(object) === REACT_FRAGMENT_TYPE
}
const REACT_FRAGMENT_TYPE = hasSymbol
  ? Symbol.for('react.fragment')
  : 0xeacb;
const hasSymbol = typeof Symbol === 'function' && Symbol.for;
```
I hope this helps!
All comments are welcomed.