/keepalive-for-react

React KeepAlive is a component that can cache the state of the component and reuse it when needed.

Primary LanguageTypeScriptMIT LicenseMIT

keepalive-for-react logo

KeepAlive for React

A React KeepAlive component like keep-alive in vue

中文 | English

NPM version NPM downloads

Attention!

  • DO NOT use <React.StrictMode />, it CANNOT work with keepalive-for-react in development mode. because it can lead to some unexpected behavior when you use keepalive-for-react's useOnActive hook.

  • In Router only support react-router-dom v6+

Features

  • support react 16.8+ ~ 18+
  • dramatically reduce the number of dom elements in the page
  • support for caching component state
  • simply implement, without any extra dependencies and hacking ways
  • support for custom cache rules
  • high performance, no performance loss
  • easy to use, just wrap the component you want to cache

Usage

Install

npm

npm install --save keepalive-for-react 

APIs

KeepAlive

in simple tabs

import KeepAlive from 'keepalive-for-react';

function TabsPage() {
  const tabs = [
    {name: 'Tab1', cache: true, component: Tab1,},
    {name: 'Tab2', cache: true, component: Tab2,},
    {name: 'Tab3', cache: false, component: Tab3,},
  ];
  const [activeTab, setActiveTab] = useState('Tab1');

  const page = useMemo(() => {
    return tabs.find(tab => tab.name === activeTab);
  }, [activeTab]);

  return   <div>
    <KeepAlive
      max={20} strategy={'PRE'} activeName={activeTab} cache={page?.cache}
    >
      {page && <page.component name={page.name} />}
    </KeepAlive>
  </div>
}

in react-router-dom v6+

import {useLocation, useOutlet} from 'react-router-dom';

function BasicLayoutWithCache() {
  
  const outlet = useOutlet();
  const location = useLocation();


  /**
   * to distinguish different pages to cache
   */
  const cacheKey = useMemo(() => {
    return location.pathname + location.search;
  }, [location]);


  return <div>
    <KeepAlive activeName={cacheKey} max={10} strategy={'LRU'}>
      {outlet}
    </KeepAlive>
  </div>
}

useEffectOnActive / useLayoutEffectOnActive

useEffectOnActive is a hook to listen to the active state of the component which is wrapped by KeepAlive.

import {useEffectOnActive} from 'keepalive-for-react';

useEffectOnActive((active) => {
    console.log('useOnActive', active);
}, false, []);

useKeepAliveContext

useKeepAliveContext is a hook to get the KeepAlive CacheComponent context.

import {useKeepAliveContext} from 'keepalive-for-react';

function CachedComponent() {
  
  const { active, destroy} = useKeepAliveContext();
  // active: boolean, whether the component is active
  // destroy: () => void, destroy the component from cache

  // ...
}

KeepAlive Props

interface Props {
    children: ReactNode;
    /**
     * active name
     */
    activeName: string;
    /**
     * max cache count default 10
     */
    max?: number;
    /**
     * cache: boolean default true
     */
    cache?: boolean;
    /**
     * maxRemoveStrategy: 'PRE' | 'LRU' default 'PRE'
     *
     * PRE: remove the first cacheNode
     *
     * LRU: remove the least recently used cacheNode
     */
    strategy?: "PRE" | "LRU";
    /**
     * aliveRef: KeepAliveRef
     *
     * aliveRef is a ref to get caches, remove cache by name, clean all cache, clean other cache except current
     *
     */
    aliveRef?: RefObject<KeepAliveRef | undefined> | MutableRefObject<KeepAliveRef | undefined>;

    exclude?: Array<string | RegExp> | string | RegExp;

    include?: Array<string | RegExp> | string | RegExp;

    /**
     * suspenseElement: Suspense Wrapper Component
     */
    suspenseElement?: ComponentType<{
        children: ReactNode,
    }>;

    /**
     *  errorElement: for every cacheNode's ErrorBoundary
     */
    errorElement?: ComponentType<{
        children: ReactNode,
    }>;
    animationWrapper?: ComponentType<{
        children: ReactNode
    }>

    /**
     * onBeforeActive: callback before active
     * @param name
     *
     * you can do something before active like set style for dropdown
     *
     * example:
     * ```tsx
     * // fix the style flashing issue when using Antd Dropdown and Select components, which occurs when the components are wrapped by Suspense and cached.
     *
     * // set .ant-select-dropdown .ant-picker-dropdown style to ''
     * const dropdowns = document.querySelectorAll('.ant-select-dropdown');
     * dropdowns.forEach(dropdown => {
     *     if (dropdown) {
     *         dropdown.setAttribute('style', '');
     *     }
     * });
     *
     * const pickerDropdowns = document.querySelectorAll('.ant-picker-dropdown');
     * pickerDropdowns.forEach(pickerDropdown => {
     *     if (pickerDropdown) {
     *         pickerDropdown.setAttribute('style', '');
     *     }
     * });
     * ```
     */
    onBeforeActive?: (name: string) => void
    /**
     *  containerDivRef: root node to mount cacheNodes
     */
    containerDivRef?: MutableRefObject<HTMLDivElement>
    /**
     *  cacheDivClassName: className set for cacheNodes
     */
    cacheDivClassName?: string
}

type KeepAliveRef = {
    getCaches: () => Array<CacheNode>

    removeCache: (name: string) => Promise<void>

    cleanAllCache: () => void

    cleanOtherCache: () => void
}

useKeepaliveRef

import { useKeepaliveRef } from "keepalive-for-react"

function Example() {

    const aliveRef = useKeepaliveRef()
    
    function clean(){
        aliveRef.current?.cleanAllCache()
    }
    // ...
    
    return <KeepAlive aliveRef={aliveRef} >
        ...
    </KeepAlive>
}

Full Code Usage Example

link to React Keepalive Demo Repo

Preview Online Demo: Link: https://irychen.github.io/react-keepalive-demo/

Forum

Star History

Star History Chart