1.背景
用户访问了一个列表页,点击查进入详情页,从详情页退回列表页时,需要停留在离开列表页时的浏览位置上
在 React 中,我们通常会使用路由去管理不同的页面,而在切换页面时,路由将会卸载掉未匹配的页面组件,所以上述列表页例子中,当用户从详情页退回列表页时,会回到列表页顶部,因为列表页组件被路由卸载后重建了,状态被丢失。
2.常见的解决方式
(1)手动保存状态:配合 React 组件的 componentWillUnmount 生命周期通过dva、 redux 之类的数据流管理工具对数据进行保存,通过 componentDidMount 周期进行数据恢复。这种方法需要每次手动保存与恢复数据,并且像我们自己封装的列表组件,数据的请求和渲染都是在组件上进行的,这就导致在项目没办法保存与恢复数据。
(2)通过样式控制:列表和详情页写在同一个页面上,通过样式来控制组件的显示与隐藏。这种方法代码改动大并且体验不好。
3.自动保存状态
(1)常用*分析
组件 | |
---|---|
react-live-route | 实现成本也比较高,需要注意对原始 功能的保存,以及多个 react-router 版本的兼容 |
react-keeper | 完全替换掉路由方案是一个风险较大的事情,需要较为慎重地考虑 |
react-keep-alive | 真实 KeepAlive 功能的实现 |
react-router-cache-route | 由于不再是组件卸载,所以和 TransitionGroup 配合得不好,导致转场动画难以实现 |
react-activation | 其 children 属性抽取出来,渲染到一个不会被卸载的组件内 |
(2)基于我们是配置式路由、业务代码改动最小以及组件所提供的能力,最后选择了react-activation。
import React, { Component } from 'react';
import KeepAlive, { withActivation } from 'react-activation';
import CachePage from './keepalive';
@CachePage
@withActivation
class Test extends Component {
constructor(props) {
super(props);
this.state = {
datalist: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,26, 27, 28, 29, 30],
};
}
// 类组件要配合withActivation装饰器,才能使用componentDidActivat与componentWillUnactivate 对应激活与缓存两的生命周期
// 函数组件可直接使用useActivate 与 useUnactivate hooks 钩子
componentDidActivate() {
console.log('激活页面——componentDidActivate')
}
componentWillUnactivate() {
console.log('缓存页面——componentWillUnactivate')
}
render() {
const { datalist } = this.state;
return (
<div>
{datalist.map((item) => {
return (
<p key={item}>test——{item}</p>
);
})}
</div>
);
}
}
export default Test;
/** keepalive.js **/
import React from 'react';
import KeepAlive from 'react-activation';
const CachePage = WrapperComponent => props => {
const { location:{ pathname, search } } = props;
return (
<KeepAlive name={pathname} id={`${pathname}${search}`} when saveScrollPosition='screen'>
<WrapperComponent {...props} />
</KeepAlive>
);
};
export default CachePage;
注:
1.KeepAlive没法作用到状态管理。同一个路由参数不同的页面, id不同虽然页面会多份缓存,但是如果页面数据有用model管理会造成数据错乱,因为缓存的只是页面而状态管理里面的数据变了就变了。
解决方法:可以在 model管理的数据注入进来的时候,不直接使用,可以是转成组件的 state,这样就可以跟着 KeepAlive 的状态走了。
2.KeepAlive 内部的组件才能响应缓存生命周期
3.when为true的路由会被一直保存,别忘了在适当的时候清除缓存
具体使用方法详见文档,作者写的很细https://github.com/CJY0208/react-activation/blob/master/README_CN.md