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

  1. 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

  2. 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

  3. 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-activationFixed in)
  • ContextContext failure (react-activationFixed in)
  • Error BoundariesFailure (react-activationFixed in)
  • React.Suspense & React.lazyFailure (react-activationFixed 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