Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Lazy loaded component loosing it's state (gets unmounted)

Tags:

I have the below component that loads my components when required (upon route change).

function DynamicLoader(props) {
  const LazyComponent = React.lazy(() => import(`${props.component}`));
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

And my Routes (using React-Router) looks like below:

            <Switch>
            {routes.map((prop, key) => {
              return (
                <Route
                  exact
                  path={prop.path}
                  render={() => (
                    <DynamicLoader component={prop.component} />
                  )}
                  key={key}
                />
              );
            })}
          </Switch>

This is working fine as far as mounting the component for each route goes, however it looks like with every change in the parent component, React is unmounting and remounting the lazy loaded component (instead of re-rendering). And this cause all the internal states to reset which is of course undesired. Can anyone recommend any solution please? Here is a codesandbox showing this issue.

like image 798
Asha Avatar asked Jul 04 '19 12:07

Asha


People also ask

What is lazy loading suspense How do they work?

lazy() is only loaded when it is required to be displayed. While the lazy component is loading, you should probably show some form of placeholder content, such as a loading indication. React. Suspense is a component used to surround lazy components.

Does React support lazy loading?

React. lazy() is a function that allows us to render dynamic imports in the same way as regular components. Using dynamic imports alongside the React. lazy() will enable us to import a component just before it renders on a screen.

Should I lazy load all components?

As we build code components the application grows, and the bundle gets very cumbersome in size. This can quickly make using the application very hard and especially slow, which has many disadvantages, like long load times and bad user experience. Lazy loading is a way to avoid all this and has many other advantages.

Can't perform a React state update on an unmounted component This is a no op?

Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.


1 Answers

Whenever the parent is rendered, the DynamicLoader recreates the LazyComponent. React sees a new component (not the same object), unmounts the previous one, and mounts to the new one.

To solve this issue use React.useMemo() inside DynamicLoader to memoize the current LazyComponent, and only recreates it if props.component actually changes:

const DynamicLoader = ({ component, parentUpdate }) => {
  const LazyComponent = useMemo(() => React.lazy(() => import(component)), [
    component
  ]);

  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent parentUpdate={parentUpdate} />
    </Suspense>
  );
};

Sandbox - to showcase the memoized LazyComponent, I'm passing the outer update to the HomeA component.

Since useMemo() caching is not guaranteed (react might free memory from time to time), you can write a simple lazy caching using a Map:

const componentsMap = new Map();

const useCachedLazy = (component) => {
  useEffect(
    () => () => {
      componentsMap.delete(component);
    },
    [component]
  );

  if (componentsMap.has(component)) return componentsMap.get(component);

  const Component = React.lazy(() => import(component));

  componentsMap.set(component, Component);

  return Component;
};

const DynamicLoader = ({ component, parentUpdate }) => {
  const LazyComponent = useCachedLazy(component);

  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent parentUpdate={parentUpdate} />
    </Suspense>
  );
};

Sandbox

like image 190
Ori Drori Avatar answered Nov 15 '22 05:11

Ori Drori