A Simple Way to Get Child State in the Parent Component Without Unnecessary Renders
This Post is for all those React programmers that have been cross with one particular situation.
Imagine your are building a parent component (lets call it “PARENT”), and inside that parent component you have a child component (lets call it “CHILD”). The “PARENT” component has a button, that when it’s clicked, you have to collect the internal state of “PARENT” and “CHILD” components to do something with it, like send the data/state to POST API endpoint to save it on a data base.
1- Elevating the state: The most common solution is to manage the state of the “CHILD” in the “PARENT” component, so in “PARENT” you have to manage it’s own state and his “CHILD” state. This can be a problem, because you are coupling this components and overloads the responsibility and state management on the “PARENT” component, making it more difficult to test, re-use and maintain.
2- Global State: Uses some mechanism to manage all the state from a third party library such as Redux or using Context. With this approach we remove the problems from the first solution, because it elevates the state management to a global place, but if we need to know the state of “CHILD” inside “PARENT” component, we need to subscribe “PARENT” to the global state that manage the “CHILD” state component, and while we do that, we create a more complex code, leaving one problem that where present on the first approach too: Every time the “CHILD” component change it’s state, the “PARENT” component will re-render too, and if you are not careful, all his children's will re-render as well (you can avoid that last part with React.memo() on other children's, but that is for other post).
The solution to the rescue:
For this particular situation, the “CHILD” component should manage its own state, and in a particular moment, the “PARENT” must have access to that “CHILD” state, but when the “CHILD” state changes, it can’t trigger a re-render on the “PARENT” component. Achieving that, all our problems would be solved. The good news is that we can!
How to do it with a different approach?
3- Using references: To solve this, we are going to use Hooks and fordwardRef. It’s necessary that in order to work, you need React ≥v16.8. If you are not familiar with Hooks, you should give it a try.
What we need to do in the “PARENT” is to pass a reference (lets call it “childStateRef”) to the “CHILD” component via props. That reference needs to be created on “PARENT” using the useRef() hook. That “childStateRef” is going the be the one that will have the “CHILD” state whenever we need it on the “PARENT” component. See the code below.
On the “CHILD” component, making use of a combination with React.fordwardRef and the useImperativeHandle() hook we have a way of provide to the received reference a method that returns the internal state of the “CHILD” component. See the code below.
This is it, We did it!
The code shown until now is a simple way to resolve this problem , but is not the real code used on the examples showed on animated gif. If you want to see the real code you can check this GitHub repository.
This is a React example project, to demonstrate my point on a blog post I publish. Basically, I'm trying to show here…
And if you want to see the entire application running with all the three examples, and without fork or pull the the entire code and running it yourself on your local environment, you can check this link where it is deployed. Tip: Check your developer console, it will help you to see the amount of renders on each component for each of the three solutions.
In my personal opinion, this last proposal solution is the best option for some specific situations, because it avoids unnecessary renders mainly on the parent, and when we have heavy components, this can be a huge performance improvement. Also, with this last approach, you can let the state management of each component on the component itself, allowing you to have a cleaner code to test, maintain, read and understand.