Last week, we learned how the virtual DOM works in React. One of the things we talked about was the special .children prop. Today, we're going to learn how to use the .children prop to achieve composition in React.

Composition in OOP

Before we get into composition in React, let's take a step back and look at composition in object-oriented programming (OOP). We'll create Item and List classes to help us create different types of list.

Instances of the Item class should have a name and a way of tracking if they're done or not. Inside the constructor() method, we'll assign .name and .done properties to the current instance of the class:

class Item {
constructor(name, done = false) {
this.name = name;
this.done = done;
}
}

Instances of the List class should have a name and an array of Item instances. Inside the constructor() method, we'll assign .name and .items properties to the current instance of the class:

class List {
constructor(name, items = []) {
this.name = name;
this.items = items;
}
}

When we create an instance of the List class, we'll pass in an array of Item instances:

const toDoList = new List("To Do", [
new Item("Walk dogs", true),
new Item("Buy groceries"),
new Item("Work out"),
]);

This is the idea of composition; we compose a more complex structure with instances of smaller classes.

Demo of composition in OOP.

Composition in React

The .children prop

Last week, we learned that a React element's children are made available via its special .children prop. So if we had some JSX as follows:

<div className="App">
<h1 className="App-title">Hello, World!</h1>
</div>

It would be compiled to the following vanilla JS:

React.createElement(
"div",
{ className: "App" },
React.createElement("h1", { className: "App-title" }, "Hello, World!")
);

And the virtual DOM node would be as follows. Pay special attention to the .children props:

const vNode = {
type: "div",
props: {
className: "App",
children: {
type: "h1",
props: {
className: "App-title",
children: "Hello, World!",
},
},
},
};

The ability to nest child elements in this way is a convenience; React will automatically assign them to the .children prop. But we can also assign them to the .children prop directly:

<div
className="App"
children={<h1 className="App-title" children="Hello, World!" />}
/>

Without JSX, it would be as follows. Note that we're only passing in two arguments: the element type and a props object.

React.createElement("div", {
className: "App",
children: React.createElement("h1", {
className: "App-title",
children: "Hello, World!",
}),
});

Either way, the resulting virtual DOM node is the same.

The <Item> component

Let's start with our <Item> component. We'll use destructuring assignment to assign a default value of false to the .done prop, and rest syntax so we can access the rest of the props via the props object as normal.

Note that we're nesting the .children prop inside the <span> element. This lets us nest whatever text we want inside the <Item> component instance, and it will be available via the .children prop.

function Item({ done = false, ...props }) {
const [checked, setChecked] = useState(done);

function handleChange(event) {
setChecked(event.target.checked);
}

return (
<li className="Item">
<label>
<input
className="Item-input"
type="checkbox"
checked={checked}
onChange={handleChange}
/>

<span className="Item-label">{props.children}</span>
</label>
</li>
);
}

We haven't created our <List> component yet, but we can still nest our <Item> component within a regular <ul> or <ol> element. This is one of the benefits of composition; it makes it easy to reuse our code in different ways!

<ul>
<Item done={true}>Walk dogs</Item>
<Item>Buy groceries</Item>
<Item>Work out</Item>
</ul>

The <List> component

Our <List> component will accept a .name prop which we'll use as the text content for an <h2> element.

We'll nest the .children prop inside a <ul> element. This means that when we nest instances of the <Item> component inside an instance of the <List> component, they'll become children of the <ul> element.

function List(props) {
return (
<article className="List">
<header>
<h2>{props.name}</h2>
</header>
<ul role="list">{props.children}</ul>
</article>
);
}

With our <List> component in place, we can nest instances of our <Item> component. They'll be available to the instance of the <List> component via its .children prop:

<List name="To Do">
<Item done={true}>Walk dogs</Item>
<Item>Buy groceries</Item>
<Item>Work out</Item>
</List>

Demo of composition in React.

Summary

Composition is a fundamental concept in object-oriented programming (OOP) and a powerful way to reuse code between React components. We achieve composition in React using the special .children prop.