Understanding Information Flow in React: Data Down, Action Up
When learning React, you could easily discover the allure of component-based coding and ask yourself why you didn’t make all your previous projects on this high-advantage JavaScript library. Code re-usability, consistency, and maintainability are just a few advantages React components offer. However, when first starting off, understanding the flow of information between components and how to update it can be very frustrating and easily deter someone’s interest.
In this blog, we will first go over what components, state, and props are. Then we’ll go step by step on how information gets passed down for rendering and back up to update information for a DOM re-render.
Components
To start off, we should first recap what a React component is and why mapping them out in a component tree can save you lots of headaches when figuring out the flow of information.
According to the official React documentation:
Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen.
In the Browser image above, each component, distinguished by numbers, is laid out in one single page application. They each have their own functions and contain a section of code that describes what it should render to the DOM. To the right is a UI Tree, also known as a component tree, that helps developers map out parent components, child components, and information flow of properties — props.
State v Props
State
This is information that can be mutated and lives locally in the component in which it is constructed. It can be considered the heart of the component because it is the information that ultimately gets rendered on to the DOM. You can pass down state from parent to child components through props.
Props
This is information that can not be mutated and is passed down from a parent component (information such as state or functions). It is important to note that when information gets passed down from a parent component to a child component, it can not skip “layers” of child components. So with the UI Tree image above, if component 1 wanted to pass down its information, via props, to component 4, it would have to get passed through component 2 first.
“Data down, Action up”
This phrase helps developers understand the flow of information from parent to child component and vice versa.
The first and more simple concept, “data down,” refers to the passing of data and/or functions via props from parent to child components. These props are passed down when a child component gets created. We pass data down to child components so they can render them on to the DOM.
Data is sent down to the child from the parent component with the help of props
Let’s breakdown this “data down” concept into steps:
- Parent component creates a child component
- Parent component passes down data by referencing
this.state
via props - Child component can render the data by referencing
this.props
The second and trickier concept to understand, “action up,” refers to sending data back up to the parent from the child component with the help of some action or event. Often these actions are connected to a callback function.
Data is sent back up to the parent from the child component with the help of callback functions
Let’s breakdown this “action up” concept into steps:
- A user interacts with a UI element (i.e. click on a
button
element) - An attached event listener to the UI element is triggered (i.e.
onClick)
- An attached handler to the event listener is triggered (i.e.
handleClick)
- A callback function inside the handler is triggered (i.e.
onHandleClick)
- Logic of the callback function is executed within the parent component
- State is updated inside the parent component
- Data is sent back down and the DOM re-renders
We’ll go into more detail on each step later with the example below.
Example: Incrementer App
Say we have a simple ‘Incrementer’ app that shows a number on the browser. The user will be able to increment the number by clicking on the button that says “Increment Number by 1.”
Data Down
Information is passed down from the top-level component called App
. Notice that the state includes data(a number with the value 0).
What’s passed down includes:
onHandleClick={this.incrementNumber}
, a callback functionnumber={this.state.number}
We include them inside the ParentComponent
tag. This is referred to as passing down props- “data down.”
ParentComponent
receives props from App
.
ParentComponent
renders a ChildComponent
and passes down its props to it.
ChildComponent
receives props from ParentComponent
ChildComponent
renders the number referencing this.props.number
. You can see this on the Incrementer app as the number 0.
Action Up
When the user interacts with the website, they see a button that says “Increment Number by 1.” The user would expect to see the number on screen increment by 1 when they click this button. How does the DOM update the number when the number that is rendered on the screen is a prop, data that can not be mutated? This is where it gets a bit confusing. Data can only be mutated, or updated, where it was created, in state.
In order for the DOM to update, data needs to be sent back up to where it was stored and then back down to where it gets rendered. In other words, data needs to somehow be sent from the child component back to its parent component where state was constructed, update itself, and then go back down to the child component for re-rendering. We can accomplish this through callback props.
In the “Incrementer” example, the data sent down to ChildComponent
originated from App
. The number: 0
lives in the state of App
but gets rendered in the ChildComponent
. So when a user clicks on the “Increment number by 1” button, number:0
needs to become number:1
but can only be updated in App
.
Let’s perform our “action up” concept to illustrate this.
- A user interacts with a UI element. They see “Increment Number by 1” and click on the button.
- The attached event listener to the UI element is triggered. You can see inside the
button
tag isonClick
which gets triggered on a mouse click. This then triggers a function insideChildComponent
calledhandleClick
. It’s proper naming convention to start any event handler with “handle.” This way you’ll know what this function is actually doing.
3. The attached handler to the event listener is triggered, handleClick
4. The callback function inside the handler is triggered,onHandleClick()
. It’s proper naming convention to start any callback function with the word “on” followed by the handler.
5. The logic of the callback function is executed within the parent component. This is seen in the function incrementNumber()
below. It takes the previous state number: 0
and increments the value by 1.
6. The state is updated inside the parent component. number: 0
is updated to number: 1
7. Data is sent back down to ChildComponent
and re-renders number: 1
Information flow diagram
Below is a diagram that helps illustrate the information flow with the Incrementer app example:
Happy coding! 😊