Skip to content

Releases: irychen/keepalive-for-react

v3.0.0

23 Oct 12:08
Compare
Choose a tag to compare

keepalive-for-react logo

KeepAlive for React

A React KeepAlive component like keep-alive in vue

中文 | English

NPM version NPM downloads

Features

  • Support react-router-dom v6+
  • Support React v16+ ~ v18+
  • Support Suspense and Lazy import
  • Support ErrorBoundary
  • Support Custom Container
  • Support Switching Animation Transition with className active and inactive
  • Simply implement, without any extra dependencies and hacking ways

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+

Install

npm install keepalive-for-react
yarn add keepalive-for-react
pnpm add keepalive-for-react

Usage

in react-router-dom v6+

  1. install react-router-dom v6+
npm install react-router-dom keepalive-for-react
  1. use KeepAlive in your project
import { KeepAliveRouteOutlet } from "keepalive-for-react";

function Layout() {
    return (
        <div className="layout">
            <KeepAliveRouteOutlet />
        </div>
    );
}

details see examples/react-router-dom-simple-starter

in simple tabs

npm install keepalive-for-react
const tabs = [
    {
        key: "tab1",
        label: "Tab 1",
        component: Tab1,
    },
    {
        key: "tab2",
        label: "Tab 2",
        component: Tab2,
    },
    {
        key: "tab3",
        label: "Tab 3",
        component: Tab3,
    },
];

function App() {
    const [currentTab, setCurrentTab] = useState<string>("tab1");

    const tab = useMemo(() => {
        return tabs.find(tab => tab.key === currentTab);
    }, [currentTab]);


    return (
        <div>
            {/* ... */}
            <KeepAlive transition={true} activeCacheKey={currentTab} exclude={["tab3"]}>
                {tab && <tab.component />}
            </KeepAlive>
        </div>
    );
}

details see examples/simple-tabs-starter

KeepAlive Props

type definition

interface KeepAliveProps {
    // determine which component to is active
    activeCacheKey: string;
    children?: KeepAliveChildren;
    /**
     * max cache count default 10
     */
    max?: number;
    exclude?: Array<string | RegExp> | string | RegExp;
    include?: Array<string | RegExp> | string | RegExp;
    onBeforeActive?: (activeCacheKey: string) => void;
    customContainerRef?: RefObject<HTMLDivElement>;
    cacheNodeClassName?: string;
    containerClassName?: string;
    errorElement?: ComponentType<{
        children: ReactNode;
    }>;
    /**
     * transition default false
     */
    transition?: boolean;
    /**
     * transition duration default 200
     */
    duration?: number;
    aliveRef?: RefObject<KeepAliveRef | undefined>;
}

Hooks

useEffectOnActive

useEffectOnActive(() => {
    console.log("active");
}, []);

useLayoutEffectOnActive

useLayoutEffectOnActive(
    () => {
        console.log("active");
    },
    [],
    false,
);
// the third parameter is optional, default is true,
// which means the callback will be skipped when the useLayoutEffect is triggered in first render

useKeepAliveContext

type definition

interface KeepAliveContext {
    /**
     * whether the component is active
     */
    active: boolean;
    /**
     * refresh the component
     * @param cacheKey - the cache key of the component,
     * if not provided, current active cached component will be refreshed
     */
    refresh: (cacheKey?: string) => void;
}
const { active, refresh } = useKeepAliveContext();
// active is a boolean, true is active, false is inactive
// refresh is a function, you can call it to refresh the component

useKeepaliveRef

type definition

interface KeepAliveRef {
    refresh: (cacheKey?: string) => void;
    destroy: (cacheKey: string) => Promise<void>;
}
function App() {
    const aliveRef = useKeepaliveRef();
    // aliveRef.current is a KeepAliveRef object

    // you can call refresh and destroy on aliveRef.current
    aliveRef.current?.refresh();
    // it is not necessary to call destroy manually, KeepAlive will handle it automatically
    aliveRef.current?.destroy();

    return <KeepAlive aliveRef={aliveRef}>{/* ... */}</KeepAlive>;
}
// or
function AppRouter() {
    const aliveRef = useKeepaliveRef();
    // aliveRef.current is a KeepAliveRef object

    // you can call refresh and destroy on aliveRef.current
    aliveRef.current?.refresh();
    aliveRef.current?.destroy();
    return <KeepAliveRouteOutlet aliveRef={aliveRef} />;
}

Development

install dependencies

pnpm install

build package

pnpm build

link package to global

pnpm link --global

test in demo project

cd demo
pnpm link --global keepalive-for-react

v2.0.12

29 Aug 03:47
757c2e7
Compare
Choose a tag to compare

New Feature in v2.0.12

Now, you can refresh you cached component !!!

in KeepAlive aliveRef

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

function Example() {

    const aliveRef = useKeepaliveRef()
    
    function refresh(){
        aliveRef.current?.refresh()  // default refresh current showing cached component without the name param
        //  aliveRef.current?.refresh("your refresh component name") 
    }
    // ...
    
    return <KeepAlive aliveRef={aliveRef} >
        ...
    </KeepAlive>
}

in cached component

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

function CachedComponent() {
  
  const { refresh } = useKeepAliveContext();
  // refresh: (name?: string) => void, refresh the component same as `aliveRef.current?.refresh()`
}

v2.0.6

17 Apr 13:09
Compare
Choose a tag to compare

New Release: Version v2.0.6

Introducing new hooks useEffectOnActive, useLayoutEffectOnActive

/**
 * a hook that executes a callback function when the active state of the cache component changes.
 * The callback can optionally return a cleanup function that will be executed on component unmount or before the callback is executed again.
 *
 * @param cb A callback function to be executed when the active state changes. It receives the current active state as a parameter. If it returns a function, that function will be used as a cleanup callback.
 * @param skipMount If true, the callback (and potentially its cleanup) is not executed on the initial component mount. Defaults to false.
 * @param deps Dependencies to be passed to the useEffect hook.
 */
export const useEffectOnActive = (cb: (active: boolean) => void | (() => void), skipMount = false, deps: DependencyList): void => {
    const { active } = useCacheComponentContext() 
    const isMount = useRef<boolean>(false)
    useEffect(() => {
        if (skipMount && !isMount.current) {
            isMount.current = true
            return
        }
        const destroyCb = cb(active)
        return () => {
            if (isFn(destroyCb)) {
                destroyCb()
            }
        }
    }, [active, ...deps])
}

/**
 * @name useLayoutEffectOnActive
 * a hook that executes a callback function when the active state of the cache component changes.
 * The callback can optionally return a cleanup function that will be executed on component unmount or before the callback is executed again.
 *
 * @param cb A callback function to be executed when the active state changes. It receives the current active state as a parameter. If it returns a function, that function will be used as a cleanup callback.
 * @param skipMount If true, the callback (and potentially its cleanup) is not executed on the initial component mount. Defaults to false.
 * @param deps Dependencies to be passed to the useLayoutEffect hook.
 */
export const useLayoutEffectOnActive = (cb: (active: boolean) => void | (() => void), skipMount = false, deps: DependencyList): void => {
    const { active } = useCacheComponentContext() 
    const isMount = useRef<boolean>(false)
    useLayoutEffect(() => {
        if (skipMount && !isMount.current) {
            isMount.current = true
            return
        }
        const destroyCb = cb(active)
        return () => {
            if (isFn(destroyCb)) {
                destroyCb()
            }
        }
    }, [active, ...deps])
}

v2.0.5

10 Apr 01:14
Compare
Choose a tag to compare

New Release: Version v2.0.5

Introducing new hooks:

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

  removeCache: (name: string) => void

  cleanAllCache: () => void

  cleanOtherCache: () => void
}
import { useKeepaliveRef } from "keepalive-for-react"

function Example() {

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

v2.0.0

21 Mar 08:04
Compare
Choose a tag to compare

New Release: Version v2.0.0

Introducing new hooks:

  • useOnActive
  • useKeepAliveContext

Improvements:

  • The useOnActive hook no longer requires the domRef parameter.
  • useKeepAliveContext now allows you to access the active state of the cached component and provides a destroy method to clear the component's cache directly.