What is state saving?
Assume the following scenario:
Mobile terminal, the user access to a list of pages, pull-up browse list page in the process, as the scrolling height increase gradually, the data will also be in the form of the bottom of the page loading gradually increased, browse the list of pages to a location, the user sees the project of interest, click to view the details, enter the details page, from the details of return a list of pages, You need to stay where you were when you left the list page
Similar data or scenarios include forms that have been filled in but not submitted, function labels that can be switched or closed in the management system, etc. Such data gradually changes or increases with user interaction, which can be understood as states. In the process of interaction, if it is necessary to leave the interaction scene temporarily for some reasons, the states need to be saved
In the React, we often use routing to manage different pages, and switch on the page, routing will uninstall not matching page components, so the above list page example, when a user from the details page returned list page, will be back to the top of the list page, because components are routing list page rebuilt after unloading, state is lost
How to save state in React
In Vue, it is very easy to save state through the
tag, which caches inactive component instances rather than destroying them
React does not have this function. Some people have officially mentioned function issues, but the official said that this function is easy to cause memory leakage, so we will not consider supporting it for the time being, so we need to find our own way
The common solution is to manually save the status
Manually storing state is a common solution. You can use the React component’s state management layer (such as redux) to store data and recover data using the React componentDidMount cycle
When there is a small amount of state that needs to be saved, this is a quick way to do what we need to do, but when there is a large amount of data or a volatile situation, manually saving the state becomes a chore
As programmers, of course, we should be as lazy as possible, so that we don’t have to worry about saving and restoring data every time, we need to figure out how to save state automatically
Automatic state saving via routing (usually usedreact-router)
Since the loss of state in React is caused by uninstalling components during route switchover, we can try to change the rendering behavior of components by routing mechanism
We have the following ways to implement this feature
-
Rewrite the
component, see react-live-route
Rewriting can do what we want, but it can be expensive, requiring care to preserve the original
functionality and compatibility with multiple React-Router versions
-
To rewrite the routing library, see react- Keeper
The cost of rewriting the routing library is not affordable for the average developer, and completely replacing the routing scheme is a risky business that needs to be considered carefully
-
For an extension based on the existing behavior of the
component, see react-router-cache-route
After reading the
source code, using either the Component or render attribute can prevent the Route from being unloaded in the event of a mismatch
But using the children property as a method, we have the possibility to manually control the rendering behavior. The key code is here github.com/ReactTraini…
// Excerpt from the render function in the Route component if (typeof children === "function") { children = children(props); // When children is a function, children will be called to get the actual rendering result if (children === undefined) {... children =null; }}return ( <RouterContext.Provider value={props}>{children && ! isEmptyChildren(children) ? Children // When children exist, use children to render: props. Match? component ? React.createElement(component, props) : render ? Render (props) : null // Rendering (props) : null // rendering (props) : null // rendering (props) : null // rendering (props) : null // rendering (props) : null // rendering (props) : null // rendering (props) : null</RouterContext.Provider> ); Copy the code
Based on the above source code exploration, we can expand
and adjust the mismatch behavior of
from uninstallation to hiding, as shown below
<Route exact path="/list"> {props => ( <div style={props.match ? null : { display: 'none' }}> <List {. props} / > </div> )} </Route> Copy the code
The above is the simplest adjustment method. In actual situations, we also need to consider the component error caused by match null in the hidden state. Besides, since the component is no longer unloaded, it does not work well with TransitionGroup, which makes it difficult to realize the transition animation
When react-router-cache-route is used, the result is as follows:
The above explored the possibility of automatic state saving through routing, as well as the existing implementation, but ultimately is not a real, pure KeepAlive function. Next we will try to explore the implementation of real KeepAlive function
Simulated real<KeepAlive>
function
Here are the expected uses
function App() {
const [show, setShow] = useState(true)
return (
<div>
<button onClick={()= >setShow(show => ! show)}>Toggle</button>
{show && (
<KeepAlive>
<Test />
</KeepAlive>
)}
</div>)}Copy the code
React uninstalls components within the inherent component hierarchy, so we need to extract the children property of the
component and render it into a
component that will not be uninstalled. This function can be achieved by using DOM manipulation to move the real contents of
into the corresponding
Here is an example of a minimal
implementation of less than 70 lines: minimal implementation
Here’s how react-Activation works
Online sample
The following figure shows the implementation principle of
In the actual implementation process, many problems were encountered due to the breaking of the original React hierarchy, for example
- Render lag (
react-activation
Fixed in) Context
Context failure (react-activation
Fixed in)Error Boundaries
Failure (react-activation
Fixed in)React.Suspense
&React.lazy
Failure (react-activation
Fixed in)- React Composite event bubble failure
- Other features not discovered
However, most of the above problems can be fixed through the bridging mechanism. For details, please refer to issues here
A similar, earlier implementation is react-keep-alive
conclusion
State caching is a common requirement in applications. When the amount of data to be processed is small, manual state caching can solve most of the problems. However, when the situation is complex, it is necessary to try to separate the caching function to achieve better separation of concerns during business development
The current implementation has its own problems, but its exploration process is very interesting, the best way is still the official support, but not too high expectations at present