Composition in React
Boluwatife Fakorede
Posted on April 18, 2021
React implements a unidirectional pattern for data flow for building components. The pattern itself is not unique to react but followed strictly in react.
Unidirectional data flow means that data can only flow in one way alone.
Following the definition and if experienced with react, you would have noticed that the child component cannot pass data to the parent component; hence, data only flows in one way, “FROM the parent TO the child.”
If we need to modify the child that influences the parent or share state between two child components, we use props.
Let’s see a trivial example.
import React from "react";
import { Header } from "./components/header";
export default function App() {
const [loggedIn, setLoggedIn] = React.useState(false);
const handleSetLoggedIn = () => {
setLoggedIn(!loggedIn);
};
return <Header loggedIn={loggedIn} handleSetLoggedIn={handleSetLoggedIn} />;
}
In the example above, we make some assumptions that the App function is responsible for handling login, and it passes the “loggedIn” state and the “handleSetLoggedIn” function to Header.
Let’s investigate the Header component and see how it makes use of these props.
import React from "react";
import { Profile } from "./profile";
function Header({ loggedIn, handleSetLoggedIn }) {
return (
<>
<Profile loggedIn={loggedIn} handleSetLoggedIn={handleSetLoggedIn} />
</>
);
}
export { Header };
Above, we can see that the Header component doesn't make use of the props but passes them to the Profile component that makes use of them. This situation is known as props drilling.
Props drilling
Props drilling is passing props to a child component that doesn’t need the props but helps to pass it down to its own child component, which might pass it to its own child component because it doesn’t need it as well till it gets to the child that really needs the props.
The problem with props drilling is that things get messy, and debugging might become difficult quickly.
A good solution might be to reach out for the context API or Redux, but that is not needed to solve this problem.
We could use composition (component composition) to our advantage. In fact, the React team says:
What is composition?
Composition is the act of assembling components or elements to build a whole.
React gives a powerful tool that helps with composition, which is children-props.
We can easily refactor our Header component like this.
import React from "react";
function Header({children}) {
return (
<>
{children}
</>
);
}
export { Header };
The children’s prop is available on every component. It contains the content between the opening and closing tags of a component.
Now, our Header component is a wrapper for the children we choose to render in it.
This gives us the power to refactor our App component render in “App.js.”
import React from "react";
import { Header } from "./components/header";
import { Profile } from "./components/profile";
export default function App() {
const [loggedIn, setLoggedIn] = React.useState(false);
const handleSetLoggedIn = () => {
setLoggedIn(!loggedIn);
};
return (
<Header>
<Profile loggedIn={loggedIn} handleSetLoggedIn={handleSetLoggedIn} />
</Header>
);
}
We have solved the issue with props drilling by making use of composition.
This pattern also gives us the power to build more scalable reusable components, especially when building a tool, a library, or components used by a large number of people with varying needs.
The next article will build a card component and see how composition can be an exciting pattern to think about and use often.
Thank you for reading.
Posted on April 18, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.