Saturday, September 18, 2021
Initializing state with props.
Devslopes Academy; Week 9 Assignment: Publish an article on Medium.
Article about “something I learned this week”.
The Module is 9, React 101. I’ll be talking about what I learned from mostly the project of the Module, which is to build a debt free calculator using React.
First of all, the problems I encountered: I wanted to use props (from parent component) to initialize state of a child component. But once in the child component (ie, the user interacts with the elements of the child component) I wanted to be able to set the state variables from button and input elements of the child component (so setState with props and then from input field/button).
So far so good except that the user can also interact with the parent component while both components are displayed on the screen which means that now the parent component’s props have new values to send to the child component.
Well guest what? React doesn’t update the state of the child component with the parent props new values. (Not automatically anyway, headache number 1 before I realized that)
And two: where to set the state variables with the props in the first place? Not in the constructor because the child component is created before it receives any props so the state variables don’t have the prop values yet. In the render method? But the state variables won’t update with new props values if there’s ever new props values, like I mentioned before.
So that’s where I learned and started to understand about lifecycle methods with React. And render is one of them but “it should be pure, meaning that it does not modify component state…” (see https://reactjs.org/docs/react-component.html).
ComponentDidMount() is another lifecycle method but the code inside of it is executed only one time and I wanted to update state variables whenever the parent component props are changed. So that won’t do.
“ComponentDidUpdate() is not called for the initial render.” (see https://reactjs.org/docs/react-component.html).
So I chose the static method getDerivedStateFromProps() which “is invoke right before calling the render method, both on the initial mount and on subsequent updates.” (see https://reactjs.org/docs/react-component.html).
Then I read an article about getDerivedStateFromProps() that suggests to use it sparingly because of few common bugs when using it. (see https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html). But fortunately offered also some solutions to avoid those common bugs if we still wanted to use it.
Solution 1. Fully controlled component: meaning put the child component state variables with the parent component. After trying it I realize that my design was getting to complicated. Remember that I wanted to also modify state variables inside the child component also, so that quickly didn’t work out (even though it gave me headaches by shifting state variables from parent component back to child component a few times…)
Solution 2.Fully uncontrolled component with a key: meaning the state variables are still with the child component but the parent component will send an extra prop named key which would have a different value whenever new props values are changed. This will basically create a new instance of the child component each time the props values are updated.
This gave me a bigger headache than before and the reason is this: the child component has a transform effect of sliding down and sliding up. That transform effect is triggered from the parent component, using document.querySelector. Now by creating a new instance of the child component I suddenly no longer have the original component to apply the transform effect but a new instance of the component which I don’t have access to since it’s a new DOM node…
Fortunately the article addressed the case of it if not working:
key doesn’t work for some reason (perhaps the component is very expensive to initialize), a workable but cumbersome solution would be to watch for changes to “userID” in
getDerivedStateFromProps:" (see https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html)
So that’s what I did (not so cumbersome imho). I still used a key prop (id) from the parent component which I change every time props are updated and the code inside the getDerivedStateFromProps method “must be wrapped in a condition like in the example above, or you’ll cause an infinite loop” (see https://reactjs.org/docs/react-component.html). (see below for my coding example).
Last problem, in the child component I have two variables that are not state variables (they don’t change), but I use them in setting the state variables. So I automatically put in them in the constructor.
Now the React lifecycle method getDerivedStateFromProps() is a static method which means it is a method of the class and not of the instance of the class so I had to go back to the first article (https://reactjs.org/docs/react-component.html) and find out how to solve this also, from the article:
“This method doesn’t have access to the component instance. If you’d like, you can reuse some code between getDerivedStateFromProps() and the other class methods by extracting pure functions of the component props and state outside the class definition.”
So I then put those constants outside the class definition, in the file. It gave me an example of why and when we need to put variables outside the class definition.
Here’s my coding example with outside class definition variables, and getDerivedStateFromProps method using a condition (the id prop):
And here a screenshot of the program: