Click to share! ⬇️

composing react components

When we talk about composition, we are talking about using smaller pieces to assemble a larger cohesive unit. In React, elements are used to create components. In other words, common HTML pieces like divs, spans, forms, and so on are arranged together to create a component. In talking about composing components in React, we are referring to now taking one or more components and using them to build a larger part of the application. So in this tutorial, we’ll look at how to work with a tree of components while making use of composition.


The Current Single Item

At this point we are using create react app to host a simple react project. The index.js file and item.jsx file are shown below, which gives us the associated output.

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import "bootstrap/dist/css/bootstrap.css";
import Item from "./components/item";

ReactDOM.render(<Item />, document.getElementById("root"));
import React, { Component } from "react";

class Item extends Component {
  state = {
    count: 0
  };

  handleIncrement = e => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <React.Fragment>
        <div className="card mb-2">
          <h5 className={this.styleCardHeader()}>{this.styleCount()}</h5>
          <div className="card-body">
            <button
              onClick={() => {
                this.handleIncrement({ item });
              }}
              className="btn btn-lg btn-outline-secondary"
            >
              Increment
            </button>
          </div>
        </div>
      </React.Fragment>
    );
  }

  styleCardHeader() {
    let classes = "card-header h4 text-white bg-";
    classes += this.state.count === 0 ? "warning" : "primary";
    return classes;
  }

  styleCount() {
    const { count } = this.state;
    return count === 0 ? "No Items!" : count;
  }
}

export default Item;

This gives us a kind of neat effect. When there are no items, the header is yellow with text. Once you click to increment the count, the header turns blue with a count of how many items exist.


Composing Components

Now instead of rendering just the single <Item/> in the react application, let’s create a new component of <Items/> (plural) which will use the singular <Item/> to compose a larger piece of functionality in the application. To start, we can add a new items.jsx file to the components directory in Visual Studio Code.
new items-jsx file react


Rendering The Containing Components

This new items.jsx component is going to render a list of item.jsx components. In order for this to work, we need to make an adjustment to the index.js file. This file will no longer import and render a single item.jsx component, but will now import this new component items.jsx and render that. Note the updates to this file.

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import "bootstrap/dist/css/bootstrap.css";
import Items from "./components/items";

ReactDOM.render(<Items />, document.getElementById("root"));

Adding Some Code To items.jsx

Our new items.jsx file does not yet have any code in it. We want to use this component to render more than one item.jsx component. We can start by simply rendering two item.jsx components like so.

import React, { Component } from "react";
import Item from "./item";

class Items extends Component {
  render() {
    return (
      <React.Fragment>
        <Item />
        <Item />
      </React.Fragment>
    );
  }
}

export default Items;

Excellent! This renders out two components which are fully isolated from each other.


From Hard Coded To State Based

In the example above, the <Item/> components are simply hard-coded right into the render method of the <Items/> component. Instead of hard coding them, we could add a new state object to the <Items/> component which will hold an array of <Item/> objects. With that, we can once again use the familiar map method to render out multiple <Item/> components.

import React, { Component } from "react";
import Item from "./item";

class Items extends Component {
  state = {
    items: [
      { id: 1, value: 0 }, 
      { id: 2, value: 0 }, 
      { id: 3, value: 0 }
    ]
  };
  render() {
    return (
      <React.Fragment>
        {this.state.items.map(item => (<Item key={item.id} />))}
      </React.Fragment>
    );
  }
}

export default Items;

This now gives us three unique components getting rendered.

Note that we included the requirement of a unique key when rendering lists in react.


Passing Data To Components

We have set up a kind of parent / child relationship now. The <Items/> component is the parent of the <Item/> component. Now, we want to be able to pass data from the parent to the child. We can do this with the props object. Every react component has a property named props which is a JavaScript object that can be used to pass data. So first up, we set an attribute on the child being rendered in the actual parent component. So in the highlighted code below, the value attribute is set using state from the parent component.


items.jsx (parent!)

import React, { Component } from "react";
import Item from "./item";

class Items extends Component {
  state = {
    items: [
      { id: 1, value: 0 }, 
      { id: 2, value: 10 }, 
      { id: 3, value: 0 }
    ]
  };
  render() {
    return (
      <React.Fragment>
        {this.state.items.map(item => (
          <Item 
            key={item.id} 
            value={item.value} 
          />
        ))}
      </React.Fragment>
    );
  }
}

export default Items;

The child can now accept that attribute from the props object. In the item.jsx file we can now use the value passed from the parent to initialize the count of each item when it is rendered. This is a key point! The data being used to initialize the component is no longer coming from its own state object, but rather from its parent’s state object which passes the data down via the prop.


item.jsx (child!)

import React, { Component } from "react";

class Item extends Component {
  state = {
    count: this.props.value
  };

  handleIncrement = e => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <React.Fragment>
        <div className="card mb-2">
          <h5 className={this.styleCardHeader()}>{this.styleCount()}</h5>
          <div className="card-body">
            <button
              onClick={item => {
                this.handleIncrement({ item });
              }}
              className="btn btn-lg btn-outline-secondary"
            >
              Increment
            </button>
          </div>
        </div>
      </React.Fragment>
    );
  }

  styleCardHeader() {
    let classes = "card-header h4 text-white bg-";
    classes += this.state.count === 0 ? "warning" : "primary";
    return classes;
  }

  styleCount() {
    const { count } = this.state;
    return count === 0 ? "No Items!" : count;
  }
}

export default Item;

Now, when the page first loads and all the components are initialized, we can see that the second element has a starting count of 10.
react child components initialized via parent state

Why is that? Because in the parent items.jsx, the state object looks like so.

state = {
  items: [
    { id: 1, value: 0 }, 
    { id: 2, value: 10 }, 
    { id: 3, value: 0 }
  ]
};

Passing Children Via Props

In addition to passing data from the parent to the child via attributes set on the item being rendered, we can also put markup in between the opening and closing tag of the item being rendered. That markup can then be fetched in the child using this.props.children. Let’s modify the markup in the parent here. We modify <Item/> to have an opening and closing tag. Then we had some html in between those opening and closing tags.


items.jsx (parent!)

import React, { Component } from "react";
import Item from "./item";

class Items extends Component {
  state = {
    items: [{ id: 1, value: 0 }, { id: 2, value: 10 }, { id: 3, value: 0 }]
  };
  render() {
    return (
      <React.Fragment>
        {this.state.items.map(item => (
          <Item key={item.id} value={item.value}>
            <div className="card-footer text-muted">
              Footer passed via prop.children!
            </div>
          </Item>
        ))}
      </React.Fragment>
    );
  }
}

export default Items;

Now check out how we access that children property here in the child component.


item.jsx (child!)

import React, { Component } from "react";

class Item extends Component {
  state = {
    count: this.props.value
  };

  handleIncrement = e => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <React.Fragment>
        <div className="card mb-2">
          <h5 className={this.styleCardHeader()}>{this.styleCount()}</h5>
          <div className="card-body">
            <button
              onClick={item => {
                this.handleIncrement({ item });
              }}
              className="btn btn-lg btn-outline-secondary"
            >
              Increment
            </button>
          </div>
          {this.props.children}
        </div>
      </React.Fragment>
    );
  }

  styleCardHeader() {
    let classes = "card-header h4 text-white bg-";
    classes += this.state.count === 0 ? "warning" : "primary";
    return classes;
  }

  styleCount() {
    const { count } = this.state;
    return count === 0 ? "No Items!" : count;
  }
}

export default Item;

This gives us the following output in the browser.
react prop children example


Making Children Dynamic

The children prop can also pass dynamic data. Here is an updated version which now displays the id of each <Item/> in the footer area.

import React, { Component } from "react";
import Item from "./item";

class Items extends Component {
  state = {
    items: [{ id: 1, value: 0 }, { id: 2, value: 10 }, { id: 3, value: 0 }]
  };
  render() {
    return (
      <React.Fragment>
        {this.state.items.map(item => (
          <Item key={item.id} value={item.value}>
            <div className="card-footer text-muted">
              Footer For Item Number <b className="badge badge-info">{item.id}</b>
            </div>
          </Item>
        ))}
      </React.Fragment>
    );
  }
}

export default Items;

dyamic data with react children


Learn More About Composing React Components


Composing React Components Summary

In this tutorial, we learned a little bit about how to compose components in React. When defining a component, the render() function can contain references to other components as we have seen above. When a component contains another component this is what we mean by composition or composing components. By using composition and props, you have the ability to customize the look and behavior of components however you see fit.

Click to share! ⬇️