UNPKG

react-fiber-keep-alive

Version:
226 lines (175 loc) 6.58 kB
# Keep-Alive for `React DOM` [![npm](https://img.shields.io/npm/v/react-fiber-keep-alive.svg?style=for-the-badge)](http://npm.im/react-fiber-keep-alive) [![downloads](https://img.shields.io/npm/dm/react-fiber-keep-alive.svg?style=for-the-badge)](https://www.npmjs.com/package/react-fiber-keep-alive) [![typescript](https://img.shields.io/badge/language-typescript-blue?style=for-the-badge)](https://www.typescriptlang.org/) [![LICENSE](https://img.shields.io/npm/l/react-fiber-keep-alive.svg?style=for-the-badge)](https://github.com/shenjunru/react-fiber-keep-alive/blob/main/LICENSE) `<KeepAlive>` is a component that maintains component state and avoids repeated re-rendering. ## ✨ Features - [x] Only based on `React Fiber` and `React Hooks`. - [x] Triggers original class component life circle. - [x] Triggers original effect hooks. - [x] Supports context updates. - [x] Supports multiple `keep-alive`. - [x] Supports `react-dom` v16.8+. - [x] Supports `react-dom` v17. - [x] Supports `react-dom` v18. ## 📦 Installation ```bash npm install --save react-fiber-keep-alive ``` ## 🔨 Usage ```JavaScript import React from 'react'; import ReactDOM from 'react-dom'; import KeepAlive from 'react-fiber-keep-alive'; const root = document.getElementById('root'); ReactDOM.render(( <KeepAlive.Provider value={root}> ... <KeepAlive name="test"> <YourComponent /> </KeepAlive> ... </KeepAlive.Provider> ), root); ``` ## 📝 API - Provide `root` container element. ```JSX <KeepAlive.Provider value={container}> ``` - Must be the root container of `render()`. - If not provided, `keep-alive` will be disabled. - Wrap your component with `<KeepAlive>` ```JSX <KeepAlive name="unique-key"> <YourComponent /> </KeepAlive> ``` - prop "name" is a required unique string used to identify the cache. - prop "ignore" is a optional boolean used to bypass and clear the cache. (since 0.5.0) - prop "onRead(name: string): void" is a optional callback after cache applied. (since 0.7.0) - prop "onSave(name: string): void" is a optional callback after cache saved. (since 0.7.0) - Wrap your component with `keepLive()`. ```JavaScript import { keepAlive } from 'react-fiber-keep-alive'; const NewComponent = keepAlive(YourComponent, (props) => { // props: the income props for `<YourComponent>` // you can use react hooks here return `unique-key`; // or return { name: `unique-key`, // other props for `<KeepAlive>` }; }); ``` - Hook: `useIgnoreKeepAlive()` returns a cache cleaner function. ```JavaScript import { useIgnoreKeepAlive } from 'react-fiber-keep-alive'; const ignoreCache = useIgnoreKeepAlive(); ignoreCache(`unique-key`); ``` - If the `render()` of class component has side effects. ```JavaScript import { markClassComponentHasSideEffectRender } from 'react-fiber-keep-alive'; markClassComponentHasSideEffectRender(ClassComponent); // Example: class Test extends React.Component { render() { // side effect here, ex: emit event here. return null; } } markClassComponentHasSideEffectRender(Test); ``` - If no need to trigger the effect hook while remounting. ```JavaScript import { markEffectHookIsOnetime } from 'react-fiber-keep-alive'; markEffectHookIsOnetime(effectHook); // Example: React.useEffect(markEffectHookIsOnetime(() => { // do something }), []); React.useLayoutEffect(markEffectHookIsOnetime(() => { // do something }), []); ``` - `KeepAlive.Context` (since 0.7.0) ```TSX import * as React from 'react'; import KeepAlive, { Context, KeepAliveCache } from 'react-fiber-keep-alive'; import LRUCache from 'lru-cache'; /// Example: Use LRU to manage the cache const YourKeepAliveProvider: React.FC<{ children: React.ReactNode; value: null | HTMLElement; }> = (props) => { const container = props.value; const context: Context = React.useMemo(() => { if (!container) { return []; } const caches = new LRUCache<string, KeepAliveCache>({ max: 10, }); return [container, caches, new Map()]; }, []); return ( <KeepAlive.Context.Provider value={context}> {props.children} </KeepAlive.Context.Provider> ); }; ``` ## 💡 Tips - Be careful the global side effects. (ex: insert global style) - Do not use `<KeepAlive>` under the `<React.StrictMode>`. - Recursive `<KeepAlive>` handled by top level `<KeepAlive>`. - If the `container` changed in `ReactDOM.createPortal(children, container)`. - All saved sub tree state will be lost. - Errors from `react-devtools` after `<KeepAlive>` remounted. - Try force refresh the components tree. (ex: updates components filter) ## 🏁 Tested ### Examples - [react-router v5](https://codesandbox.io/s/keep-alive-react-router-example-hfbbi7) ### React v16.8+ / v17 / v18 - [x] `render(children, container)` - [x] `hydrate(children, container)` ### React v18 (concurrent mode) - [x] `createRoot(container).render(children)` - [x] `hydrateRoot(container, children)` ### Class Component - [x] `Component.getDerivedStateFromProps()` - [x] `Component.getDerivedStateFromError()` - [x] `instance.componentDidMount()` - [x] `instance.getSnapshotBeforeUpdate()` - [x] `instance.componentDidUpdate()` - [x] `instance.componentWillUnmount()` - [x] `instance.render()` ### Function Component - [x] `useContext()` - [x] `useCallback()` - [x] `useEffect()` - [x] `useImperativeHandle()` - [x] `useLayoutEffect()` - [x] `useMemo()` - [x] `useRef()` - [x] `useState()` - [ ] `useDebugValue()` - [ ] `useDeferredValue()` (since v18) - [ ] `useId()` (since v18) - [x] `useInsertionEffect()` (since v18) - [ ] `useSyncExternalStore()` (since v18) - [ ] `useTransition()` (since v18) ### Other - [x] `ReactDOM.createPortal(children, container)` - [x] `React.memo()` - [x] `React.forwardRef()` - [x] `React.lazy()` - [x] `<Suspense>` - [ ] `<Offscreen>` (since v18) ## 🐛 Issues If you find a bug, please file an issue on [our issue tracker on GitHub](https://github.com/shenjunru/react-fiber-keep-alive/issues). ## 📄 License Copyright © 2022 [**Shen Junru**](https://github.com/shenjunru) • [**MIT license**](LICENSE).