2/11/23

React Suspense Fallback Keeps Rendering When Using Lazy-Loaded Routes


When a React application is loading a new lazy-loaded route or a component, it is a common practice to show a loading component with an animation to provide feedback to the user. In React, we use the Suspense component to handle this scenario, but there are times when the fallback component never unloads, and it keeps rendering on the browser.


When a Suspense fallback component never unloads, it is because there must be a child component that keeps rendering. This causes the Suspense component to continue to run, thinking that a child component in still in suspense or loading state.  This gives us the wrong sense that the Suspense component is misbehaving, when it is in fact the child components that is at fault. For a deeper understanding, let’s take a look at a real example.



ozkary react suspend component


Suspense with Declarative Routes


To demonstrate this problem, let’s first take a look at an implementation of a React application that loads some route information in a declarative approach, which is simple enough and does not introduce any rendering problems. We should also notice how the components are lazy-loaded to help us do code splitting for performance improvements on the load time and enable us to trigger the Suspense component automatically.

👍 This is a typical approach when creating apps with a simple routing structure.

Suspense with a Router Component


Let’s now take a look at a more complex scenario where we need to load the route information from a JSON configuration file. This introduces a variation on the process by adding the routes using a function. 



This code change introduces new behavior, and it causes the component to re-render multiple times. We can trace that by adding a console.log operation before returning the content. If we look at the browser console, we should see the output of our console.log call. From the application standpoint, this behavior is noticeable because the fallback component continues to show on the browser as the Suspense component does not detect that the child component is done rendering. Now that we understand the problem, how should we correct it?


👍 A child component continues to be in suspense until it stops rendering.


Adding State Management


To correct this behavior, we should clearly understand the root cause. Since we introduce a dynamic way to load the routes, the component has no way to understand its current state. It only knows that some data is being loaded every time it calls the function, and the data seems new or different. To avoid this, we need to add state management to the component, which is a React best practice when writing data driven components. Let’s refactor our code to see how state management can make a difference.


By looking at our new implementation, we can see that the route collection is now managed in a state variable. We also use an useEffect hook to load the data. This enables us to initiate the state of the component with some valid data. We also track the route collection as a dependency, and since there are no changes to the data, the state does not change, and the component completes its suspense state thus allowing the app to complete its rendering process.


Conclusion


The React Suspense component is a great feature to provide feedback to users while a component is rendering. When we add dynamic data to the application, we should understand that this impacts the state management process, which is used to signal when a component is in a suspense state. Depending on how nested your components are, a child component can continue to render, causing the Suspense fallback UI to continue to load non-stop. 


👍 To diagnose a component, you can add a suspense component around it, and we should notice only that component content to continue to use the suspense fallback animation.


Thanks for reading.



Send question or comment at Twitter @ozkary

Originally published by ozkary.com