Container components are components that encapsulate the data loading and data management for the child application.
Let’s say you have a component called StarShipInfo
component which lists the information about starship
const StarShipInfo = (id) => {
const [starShip, setStarShip] = useState(null);
useEffect(() => {
const fetchStarShip = async () => {
const response = await fetch(`https://swapi.dev/api/starships/${id}/`);
const data = await response.json();
setStarShip(data);
};
fetchStarShip();
}, [id]);
return (
starShip && (
<div>
<p>Name: {starShip.name}</p>
<p>Hyper Drive Rating: {starShip.hyperdrive_rating}</p>
<p>Manufacturer: {starShip.manufacturer}</p>
<p>Class: {starShip.starship_class}</p>
</div>
)
);
};
By using container component pattern we can separate out the data fetching logic into a separate container.
First, let’s create a separate component called StarShipInfoLoader
. The purpose of this component is to create a fetch request, wait for the request to be completed, and then render the StarShipInfo
component. The StarShipInfo
component is passed as a children prop to this component, we access the passed component and render it with data.
const StarShipInfoLoader = ({ id, children }) => {
const [starShip, setStarShip] = useState(null);
useEffect(() => {
const fetchStarShip = async () => {
const response = await fetch(`https://swapi.dev/api/starships/${id}/`);
const data = await response.json();
setStarShip(data);
};
fetchStarShip();
}, [id]);
return (
<>
{starShip &&
Children.map(children, (child) => {
if (isValidElement(child)) {
return cloneElement(child, { starShip });
}
})}
</>
);
};
This is how we can use the StarShipInfoLoader
and StarShipInfo
together.
<StarShipInfoLoader id={id}>
<StarShipInfo />
</StarShipInfoLoader>