DO NOT use
<React.StrictMode />(React v18+) DO NOT use
ReactDOMClient.createRoot, useReactDOM.renderinstead, CJY0208/react-activation#225 (comment)or You can disable
autoFreezeto work withcreateRootthough this may result in performance lossimport{KeepAlive}from'react-activation'KeepAlive.defautProps.autoFreeze=false// default 'true'
English | 中文说明
HACK Implementation of the <keep-alive /> function in Vue For React
Please also pay attention to official support <Offscreen /> in React 18.x
More stable <KeepAlive /> function with babel pre-compilation
React v16 / v17 / v18
Preact v10+
Compatible with SSR
yarn add react-activation
# or
npm install react-activationThe plugin adds a _nk attribute to each JSX element during compilation to help the react-activation runtime generate an unique identifier by render location base on react-node-key.
{"plugins": ["react-activation/babel"]}(0.11.0+) If you don't want to use Babel, it is recommended that each <KeepAlive> declare a globally unique and invariant cacheKey attribute to ensure the stability of the cache, as follows:
<KeepAlivecacheKey="UNIQUE_ID"/>Like the <Counter> component in the example
// App.jsimportReact,{useState}from'react'importKeepAlivefrom'react-activation'functionCounter(){const[count,setCount]=useState(0)return(<div><p>count: {count}</p><buttononClick={()=>setCount(count=>count+1)}>Add</button></div>)}functionApp(){const[show,setShow]=useState(true)return(<div><buttononClick={()=>setShow(show=>!show)}>Toggle</button>{show&&(<KeepAlive><Counter/></KeepAlive>)}</div>)}exportdefaultApp3. Place the <AliveScope> outer layer at a location that will not be unmounted, usually at the application entrance
Note: When used with react-router or react-redux, you need to place <AliveScope> inside <Router> or <Provider>
// index.jsimportReactfrom'react'importReactDOMfrom'react-dom'import{AliveScope}from'react-activation'importAppfrom'./App'ReactDOM.render(<AliveScope><App/></AliveScope>,document.getElementById('root'))ClassComponent works with withActivation decorator
Use componentDidActivate and componentWillUnactivate to correspond to the two states of "activate" and "unactivate" respectively.
FunctionComponent uses the useActivate and useUnactivate hooks respectively
...
importKeepAlive,{useActivate,useUnactivate,withActivation}from'react-activation'
@withActivationclassTestClassextendsComponent{
...
componentDidActivate(){console.log('TestClass: componentDidActivate')}componentWillUnactivate(){console.log('TestClass: componentWillUnactivate')}
...
}...functionTestFunction(){useActivate(()=>{console.log('TestFunction: didActivate')})useUnactivate(()=>{console.log('TestFunction: willUnactivate')})...}
...
functionApp(){
...
return({show&&(<KeepAlive><TestClass/><TestFunction/></KeepAlive>)})}...Add the
nameattribute to the<KeepAlive>tag that needs to control the cache.Get control functions using
withAliveScopeoruseAliveController.drop(name): (
dropcan only be used for nodes in the cache state. If the node is not cached but needs to clear the cache state, please userefresh)Unload the
<KeepAlive>node in cache state by name. The name can be of typeStringorRegExp. Note that only the first layer of content that hits<KeepAlive>is unloaded and will not be uninstalled in<KeepAlive>. Would not unload nested<KeepAlive>.dropScope(name): (
dropScopecan only be used for nodes in the cache state. If the node is not cached but needs to clear the cache state, please userefreshScope)Unloads the
<KeepAlive>node in cache state by name. The name optional type isStringorRegExp, which will unload all content of<KeepAlive>, including all<KeepAlive>nested in<KeepAlive>.refresh(name):
Refresh the
<KeepAlive>node in cache state by name. The name can be of typeStringorRegExp. Note that only the first layer of content that hits<KeepAlive>is refreshed and will not be uninstalled in<KeepAlive>. Would not refresh nested<KeepAlive>.refreshScope(name):
Refresh the
<KeepAlive>node in cache state by name. The name optional type isStringorRegExp, which will refresh all content of<KeepAlive>, including all<KeepAlive>nested in<KeepAlive>.clear():
will clear all
<KeepAlive>in the cachegetCachingNodes():
Get all the nodes in the cache
...
importKeepAlive,{withAliveScope,useAliveController}from'react-activation'
...
<KeepAlivename="Test">
...
<KeepAlive>
...
<KeepAlive>
...
</KeepAlive>
...
</KeepAlive>
...
</KeepAlive>...functionApp(){const{ drop, dropScope, clear, getCachingNodes }=useAliveController()useEffect(()=>{drop('Test')// ordrop(/Test/)// ordropScope('Test')clear()})return(
...
)}// or
@withAliveScopeclassAppextendsComponent{render(){const{ drop, dropScope, clear, getCachingNodes }=this.propsreturn(
...
)}}...Add the when attribute to the <KeepAlive /> tag that needs to control the cache. The value is as follows
- true: Cache after uninstallation
- false: Not cached after uninstallation
<KeepAlivewhen={false}>The 1th parameter indicates whether it needs to be cached at the time of uninstallation.
The 2th parameter indicates whether to unload all cache contents of <KeepAlive>, including all <KeepAlive> nested in <KeepAlive>.
// For example:// The following indicates that it is not cached when uninstalling// And uninstalls all nested `<KeepAlive>`<KeepAlivewhen={[false,true]}>
...
<KeepAlive>
...
<KeepAlive>...</KeepAlive>
...
</KeepAlive>
...
</KeepAlive>The return value is the above Boolean or Array, which takes effect as described above.
The final calculation time of when is adjusted to componentWillUnmount lifecicle of <KeepAlive>, the problem that most of the when do not achieve the expected effect can be avoided.
<KeepAlivewhen={()=>true}><KeepAlivewhen={()=>[false,true]}>Under the same parent node, <KeepAlive> in the same location will use the same cache by default.
For example, with the following parameter routing scenario, the /item route will be rendered differently by id, but only the same cache can be kept.
<Routepath="/item/:id"render={props=>(<KeepAlive><Item{...props}/></KeepAlive>)}/>Similar scenarios, you can use the id attribute of <KeepAlive> to implement multiple caches according to specific conditions.
<Routepath="/item/:id"render={props=>(<KeepAliveid={props.match.params.id}><Item{...props}/></KeepAlive>)}/><KeepAlive /> would try to detect scrollable nodes in its children, then, save their scroll position automaticlly before componentWillUnactivate and restore saving position after componentDidActivate
If you don't want <KeepAlive /> to do this thing, set saveScrollPosition prop to false
<KeepAlivesaveScrollPosition={false}/>If your components share screen scroll container, document.body or document.documentElement, set saveScrollPosition prop to "screen" can save sharing screen container's scroll position before componentWillUnactivate
<KeepAlivesaveScrollPosition="screen"/>Pass the children attribute of <KeepAlive /> to <AliveScope /> and render it with <Keeper />
After rendering <Keeper />, the content is transferred to <KeepAlive /> through DOM operation.
Since <Keeper /> will not be uninstalled, caching can be implemented.
<KeepAlive />needs to pass children to<AliveScope />, so the rendering of the real content will be slower than the normal situationWill have a certain impact on the function of strictly relying on the lifecycle order, such as getting the value of
refincomponentDidMount, as followsclassTestextendsComponent{componentDidMount(){console.log(this.outside)// will log <div /> instanceconsole.log(this.inside)// will log undefined}render(){return(<div><divref={ref=>{this.outside=ref}}> Outside KeepAlive </div><KeepAlive><divref={ref=>{this.inside=ref}}> Inside KeepAlive </div></KeepAlive></div>)}}
The above error in
ClassComponentcan be fixed by using thewithActivationhigh-level componentFunctionComponentcurrently has no processing method, you can usesetTimeoutornextTickto delay ref getting behavior@withActivationclassTestextendsComponent{componentDidMount(){console.log(this.outside)// will log <div /> instanceconsole.log(this.inside)// will log <div /> instance}render(){return(<div><divref={ref=>{this.outside=ref}}> Outside KeepAlive </div><KeepAlive><divref={ref=>{this.inside=ref}}> Inside KeepAlive </div></KeepAlive></div>)}}
Destructive impact on
Contextafter
react-actication@0.8.0withreact@16.3+, this question has been automatic fixedreact-actication@0.8.0withreact@17+you Need to make the following changes to achieve automatic repairimport{autoFixContext}from'react-activation'autoFixContext([require('react/jsx-runtime'),'jsx','jsxs','jsxDEV'],[require('react/jsx-dev-runtime'),'jsx','jsxs','jsxDEV'])
Versions below
react-actication@0.8.0need to be repaired manually, refer to the followingProblem reference: StructureBuilder/react-keep-alive#36
<Providervalue={1}>{show&&(<KeepAlive><Consumer>{(context// Since the rendering level is broken, the context cannot be obtained here.)=><TestcontextValue={context}/>}</Consumer></KeepAlive>)}<buttononClick={toggle}>toggle</button></Provider>
Choose a repair method
Create
ContextusingcreateContextexported fromreact-activationFix the affected
ContextwithfixContextexported fromreact-activation
... import{createContext}from'react-activation'const{ Provider, Consumer }=createContext()...// or ... import{ createContext }from'react'import{fixContext}from'react-activation'constContext=createContext()const{ Provider, Consumer }=ContextfixContext(Context)...
Affects the functionality that depends on the level of the React component, as follows
- Fix
withRouter/hooksof react-router Error Boundaries(Fixed)React.Suspense & React.lazy(Fixed)- React Synthetic Event Bubbling Failure
- Other undiscovered features
- Fix

