Understanding Inverse Data Flow
Olivia Pomeroy
Posted on March 9, 2022
This blog requires some base knowledge of Props and State in React. You can read up on both here.
What is Inverse Data Flow?
Up until this point, we have learned that the way data flows is from a parent component to a child component in the form of a prop. With inverse data flow, we are switching that flow so the data is passed up from the child component to the parent component.
How Does It Work?
In order for a child component to pass up data up, the parent must pass down a callback function to the child. The callback function will be passed down as a prop to the child (like in regular data flow). By having the function defined in the parent component, we can invoke it in the child and then the parent will have access to the passed data.
Conceptualization
This is a pretty important concept in React and something that you can use all the time. Once you get the hang of it, you can use it for component reusability, cleaner code, rendering components and help with your events. Before we jump into actually writing out code to show how it works, let's break it down to a very simplistic concept, using a situation many may be familiar with outside of code. Take a look at this diagram and below we will walk through how it helps explain inverse data flow.
We have our parent (mom) and two of her children (Bob and Sally). Mom gives Bobby some toys (our state data) and Bobby now has access to those toys. Bob's sister wants access to toys as well so she asks Bob. Bob says no- this is important to note because Children cannot pass data to another child component. It must pass through the parent first and then to the child. Sally asks mom for some toys and mom goes to Bob, who has all the toys(data). She puts out her hand (callback function) and Bob hands her a toy(invoking callback and passing data back up to parent). Mom now has possession of the toy, and she could hold it and do something else with it, but instead she passes it back down to Sally (setting a new state). To put it into a hierarchical structure, it'll look like this:
Seeing it in Code
Now with that visualization of how data is passed, let's walk through an example of code.
We are starting with an App component (our parent), which has GroceryItemsList and GroceryCart as children components. We are keeping State in our App component (an array of groceryItems)and passing it down to GroceryItemsList, and then iterating over it so it creates a list of individual grocery items(an object). Our goal is to be able to click on a GroceryItem and have it added to our GroceryCart component. This is visualization of our data flow:
Let's start by making a callback function in our App component and passing that as a prop to our GroceryItemsList.
function onGroceryItemClick(item) { //this is our callback function
console.log("We are adding this to GroceryCart", item) //making sure it passes it down and back up smoothly
}
return (
<div>
<GroceryItemsList
onGroceryItemClick={onGroceryItemClick} //we are passing our callback function as a prop
groceries={groceries}
/>
<GroceryCart />
</div>
)
}
export default App;
Now we need to go into our GroceryItemsList and destructure our prop, and pass it along to our GroceryItem. Of note: we are passing along our onGroceryItemClick callback function as a prop but naming it handleGroceryItemClick to make it clearer what it will be doing.
function GroceryItemsList({onGroceryItemClick, groceries}) {
const groceryItem = groceries.map((groceryObj) => {
<GroceryItemCard
key={groceryObj.id}
groceryItem={groceryObj}
handleGroceryItemClick = {onGroceryItemClick} //passing callback down another child level
/>
));
return(
<div>
{groceryItem}
</div>
Now in our GroceryItemCard, we are going to take that callback prop and give it the data it needs to invoke it.
function GroceryItemCard({groceryItem, handleGroceryItemClick}) {
const {name, description, price, image} = groceryItem
return(
<div onClick={()=> handleGroceryItemClick(groceryItem)}> //This says 'when we click on the GroceryItemCard we are going to invoke our function and send it a groceryItem (an object)
<img src={image} alt={name} />
<h5>{name}</h5>
<p>{description}</p>
<p>{price}</p>
</div>
We now see that when we click on a GroceryItemCard it is console.logging:
We are adding this to GroceryCart
{id:1, item:'Milk', price:'$3.99', brand:'Lucerne', image:'https://media.istockphoto.com/photos/kefir-milk-or-turkish-ayran-drink-are-poured-into-a-glass-cup-from-a-picture-id1198789194?b=1&k=20&m=1198789194&s=170667a&w=0&h=f85XhCzCWQhdFn0LaRckyOkJui88sYNoctN9zEmS4y0=' }
We have successfully passed down a callback function and invoked it from child to parent! That's inverse data flow!!
Now that the parent has the GroceryItem object, it can set the state of the GroceryCart to that object and render it.
function onGroceryItemClick(item) { //this is our callback function
setGroceryCart([...cart, item]) //Adding item to our GroceryCart by setting its state
}
return (
<div>
<GroceryItemsList
onGroceryItemClick={onGroceryItemClick}
groceries={groceries}
/>
<GroceryCart />
</div>
)
}
export default App;
And there we go- Inverse Data Flow. In my experience inverse data flow works really well for events (onChange, onClick, etc (especially search bars or any kind of filters)). It helps with component reusability and organizing where we put State in our library. It is a tricky concept to understand, but what's helped me is to make sure you name props appropriately and work from top to bottom, from parent to child, so you can keep track of what is being passed down.
References:
https://reactjs.org/docs/thinking-in-react.html
Posted on March 9, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.