React component provides several "lifecycle methods" that you can hook in at particular times. This lifecycle diagram is very useful that you can use it as a cheat sheet for reference. Recently, I ran into a very complex component that I found it hard to wrap my head around . The most difficult part of that component is asynchronously fetching data in componentDidUpdate method in certain conditions. At the same time, shouldComponentUpdate method should be used properly to avoid redundant rendering. Therefore, I created a demo to simplify the props and state and retain the core logic. This demo has a button. When the button is clicked, the demo generates a random number consecutively ranging from 0 to 5 until the difference between current number and previous number is not greater than 1 or it throws an error.
I made use of a few console APIs to illustrate what happens in every lifecycle method and when they are called. Specially console.groupStart() and console.groupEnd() help me to visualize what happened in each method and cycle.
Let's go through how this.setState works asynchronously. When the loading is set to true in componentDidUpdate method, it immediately goes to shouldComponentUpdate. If the condition is true, then it jumps to the render method.
You can notice that the Promise callback of updating number runs outside didComponentUpdate method. This state update then triggers another round of component rendering. Due to the nature of asynchronous update, you should always get a previous component state from the this.setState callback arguments instead of using this.state. Otherwise, you may not get the consistent state for update.
this.setState(prevState => ({
number: number,
status: [...prevState.status, number],
loading: false,
}));
The last thing should be noted is the componentDidCatch method and the ErrorBoundary component. I found the component didn't catch the error thrown by its child component which got me confused. I later found the answer. What it says is that error boundaries only catch errors in lifecycle methods. If the error occurs in a Promise callback, it technically doesn't happen inside the lifecycle methods. So the error boundary won't catch it. In this case, you are supposed to handle the error for the Promise callback within the scope.