Passing Data Between React Components

Understanding how to pass data between components is crucial in developing a React app. There are several ways we can pass data between components; we can pass data from parent to child component and vice versa. We can also pass data between siblings or distant-related components. In this article I will show basic examples of each method.

A. Passing state down

Passing state down is very simple. We can give React component any kind of property. The parent component can easily set value to its child component.

Below are two components to demonstrate passing down value from parent to child.

Parent component

// Parent
const SampleA = () => (
<div>
<h2>Sample A</h2> <p>This component is sending color values to its children</p> <SampleAChild identity={1} selectedColor={'red'}/>
<SampleAChild identity={2} selectedColor={'green'}/>
<SampleAChild identity={3} selectedColor={'blue'}/>
</div>
);

Child component

const SampleAChild = props => (
<div>
<span
style={ {backgroundColor: props.selectedColor} }
>
{ `Child ${props.identity}` }
</span>
</div>
);
Passing down data from parent to child

“Prop-drilling”

Prop-drilling is when we try to pass data to down to a distant descendant. It is possible, but not recommended. Prop-drilling can make testing cumbersome and the application prone to bug. If the distance between ancestor and descendant components is too high, try using Context instead. We’ll discuss this in final part of the article.

B. Lifting state up

We can also use a component’s prop to send value to its parent. Consider it as using a “callback”. The parent send a function as a prop to its child. The child then executes the passed down function. Because the function is actually the parent’s method, the parent will receive the value passed by the child when it is executing this “callback” function.

In this example, the parent passes down its childEventHandler function to its child.

Parent component

const SampleB = () => {  const [value, setValue] = useState('...');  // the "callback" function passed down to the child
const childEventHandler = eventData => {
setValue(eventData);
};
return (
<div>
<h2>Sample B</h2> <h3>{value}</h3> <SampleBChild onMountedHandler={childEventHandler}/> </div>
);
};

Child component

const SampleBChild = props => {

// call the "callback" function when this component is mounted
useEffect(() => {
props.onMountedHandler('Hello from Down Under 🇦🇺🦘');
});

return (
<div>
<p>
This child component is sending data to
its parent's <code>h3</code> when it is mounted.
</p>
</div>
);
};
Lift data up from child to parent

C. Using Context

A typical React app usually has a complicated tree of components. Using the methods we’ve discussed (props, state lifting), sharing data between components can get really cumbersome real quick. When there is a situation where we need to share data between distant components, we can use Context. We can think of Context as a “global” state shared with all its children components.

In the example below, we have four components. The top component, SampleC has a Context called SampleCContext. This will store the “global” data shared with its children. One of the child component, SampleCChild01 triggers a data change in the Context. All the components, including the parent, will receive the data.

Context

import React from "react";// create the context
const SampleCContext = React.createContext({});
export default SampleCContext;

Parent

const SampleC = () => {  // provide a method to change the context
const [data, changeData] = useState('');
return (
<SampleCContext.Provider value={{data, changeData}}>
<div>
<h2>Sample C</h2> <p>{data}</p> <SampleCChild01/>
<SampleCChild02/>
</div>
</SampleCContext.Provider>
);
};

Child 1

const SampleCChild01 = () => {  const {data, changeData} = useContext(SampleCContext);  const clickHandler = e => {
e.preventDefault();
changeData('THIS IS THE DATA');
}
return (
<div>
<h3>Sample C - Child 01</h3>

<button onClick={clickHandler}>SEND DATA</button>
</div>
);
};

Child 2

const SampleCChild02 = () => {  const {data} = useContext(SampleCContext);  return (
<div>
<h3>Sample C - Child 02</h3>
<p>{data}</p>
<SampleCChild0201/> </div>
);
};

Child of Child 2

const SampleCChild0201 = () => {  const {data} = useContext(SampleCContext);  return (
<div>
<h3>Sample C - Child of Child 02</h3>
<p>{data}</p>
</div>
);
};

Conclusion

Passing down and lifting state up are the most common way to share data between React components. It is highly recommended to use them in order to make our components as simple as possible. Only use Context when it is absolutely necessary. If you’re only passing data between two components, try not to use Context.

The example codes on this article are available here. If you have any question, you can always reach out to me on Twitter. Happy coding!

Programmer