React Child Component Cheatsheet đź“„

React Child Component Cheatsheet đź“„

2019, Nov 28    

Table of Contents


1. Nested Components

Nested Components is children nested inside parent components. It simply help us create more complex UI structures.

For instance, a pseudo of nested html tags.

<html>
  <toolbar></toolbar>
  <content>
    <table></table>
  </content>
</html>

2. Child Components

For instance, we have two <Item /> components nested inside a <List /> component.

Child Components

<List>
  <Item />
  <Item />
</List>

We can use props.children to access the nested children components of a parent component.

Class List extends React.Component {
  render() {
    return <>{this.props.childrent}</>;
  }
}

Live demo is available on CodePen.

2.1. Everything Can be a Child Component

In React, children don’t have to be components, they can be anything.

For instance, we can pass <List /> component some text as child.

<List>Hello World!</List>

2.2. Function as a Child Component

We can pass a render function to a component as the children prop.

<Executor>
  {(msg) => <p>Hello {msg}!</p>}
</Executor>

And the Executor component will look something like this.

class Executor extends React.Component {
  render() {
    return this.props.children('World');
  }
}

Live demo is available on CodePen.

3. Manipulating Children Components

props.children can be any type, such as array, function, object, etc.

React provides a bunch of utilities to manipulate any type of props.children as following.

3.1. Looping Children Components

React provides two utilities React.Children.map and React.Children.forEach to loop over children components.

React.Children.map

Return: a child, null, or undefined.

React.Children.map

class IgnoreSecondChild extends React.Component {
  render() {
    const children = this.props.children;
    return (
      <div>
        {React.Children.map(children, (child, index) => {
          if (index == 1) return; // Ignore the second child
          return child;
        })}
      </div>
    );
  }
}

<IgnoreSecondChild /> components maps over all its children but ignoring the second child and returning all others.

<IgnoreSecondChild>
  <h1>Child1</h1>
  <h1>Child2</h1> { /* Ignore the second child */ }
</IgnoreSecondChild>

React.Children.map

Live demo is available on CodePen.

3.2. Counting Children Components

React provides React.Children.count utilities to count any type of children.

class ChildrenCounter extends React.Component {
  render() {
    return <p>React.Children.count(this.props.children)</p>;
  }
}

It would returns the number of children no matter what type they are.

<ChildrenCounter>
  Hello World!
</ChildrenCounter>

Live demo is available on CodePen.

3.3. Converting Children Components to an Array

React provides React.Children.toArray utilities to convert the children to an array. This would be useful if you needed to sort the children component.

class ChildrenSorter extends React.Component {
  render() {
    const child = React.Children.toArray(this.props.children);
    return <p>{child.sort().join(' ')}</p>;
  }
}

It would render the sorted strings.

<ChildrenSorter>
  {'map'}{'forEach'}{'count'}{'toArray'}
</ChildrenSorter>

Live demo is available on CodePen.

3.4. Enforcing Only One Child Component

React provides React.Children.only utilities to verifies that children has only one child (a React element) and returns it. Otherwise this method throws an error.

class Executor extends React.Component {
  render() {
    return React.Children.only(this.props.children());
  }
}

Live demo is available on CodePen.

4. Modifying Children Components

For instance, we have a <RadioGroup /> component which contain many <RadioButton /> components (in raw html <input type="radio"> inside a <label>).

render() {
  <RadioGroup>
    <RadioButton value="1">1</RadioButton>
    <RadioButton value="2">2</RadioButton>
    <RadioButton value="3">2</RadioButton>
  </RadioGroup>
}

Modifying Children Components

The inputs are not grouped as they don’t have the same name attributes.

We can modify and assign a name property to every single <RadioButton /> components as following.

render() {
  <RadioGroup>
    <RadioButton name="rate" value="1">1</RadioButton>
    <RadioButton name="rate" value="2">2</RadioButton>
    <RadioButton name="rate" value="3">2</RadioButton>
  </RadioGroup>
}

But what we want is to pass the <RadioGroup /> components a unique name and its child will get and have its name automatically.

4.1. Modifying Children Props

In <RadioGroup /> components we will add a new method called renderChildwhere we will modify the children props.

class RadioGroup extends React.Component {

  renderChildren() {
    // TODo: Change the name prop of all children
    // to this.props.name
    return this.props.children; 
  }

  render() {
    return (
      <div>
        {this.renderChildren()}
      </div>
    )
  }
}

We do map loop over the children components to get each individual child in renderChildren method.

renderChildren() {
  return React.Children.map(this.props.children, child => {
    // TODO: Change the name prop of all children
    // to this.props.name
    return child;
  })
}

So far, How can we modify the children properties?

4.2. Cloning Elements

React provides React.cloneElement utilities to clone and return a new React element.

React.cloneElement takes 2 arguments, an element we want to clone and an object of props we want to be set on the cloned element.

For instance

const cloned = React.cloneElement(element, {
  name: 'rate'
});

The cloned element will now have the name prop set to “rate”.

Get back to <RadioGroup /> components, we will clone each child and set the name prop of the cloned child to this.props.name.

renderChildren() {
  return React.Children.map(this.props.children, child => {
    return React.cloneElement(child, {
      name: this.props.name
    })
  })
}

Then we pass a unique name to each <RadioGroup /> components.

<RadioGroup name="rate">
  <RadioButton value="first">First</RadioButton>
  <RadioButton value="second">Second</RadioButton>
  <RadioButton value="third">Third</RadioButton>
</RadioGroup>

Cloning Elements

Instead of manually having to set the name attribute to each components, we just tell our <RadioGroup /> what we want to the name to be.

The complete​​​ instance.

class RadioGroup extends React.Component {

  renderChildren() {
    return React.Children.map(this.props.children, child => {
      return React.cloneElement(child, {
        name: this.props.name
      });
    })
  }

  render() {
    return (
      <div className="group">
        {this.renderChildren()}
      </div>
    )
  }
}

class RadioButton extends React.Component {
  render() {
    return (
      <label>
        <input type="radio" value={this.props.value} name={this.props.name} />
        {this.props.children}
      </label>
    );
  }
}

ReactDOM.render(
  <RadioGroup name="rate">
    <RadioButton value="first">First</RadioButton>
    <RadioButton value="second">Second</RadioButton>
    <RadioButton value="third">Third</RadioButton>
  </RadioGroup>, 
  document.getElementById('app')
);

Live demo is available on CodePen.

References

Thanks for reading ❤
Say Hello! Twitter | Github | LinkedIn | Facebook | Instagram