当前位置: 首页 > 科技观察

React中如何实现状态自动保存?

时间:2023-03-11 23:34:17 科技观察

什么是状态保存?假设如下场景:在移动端,用户访问列表页面。在向上滚动浏览列表页的过程中,随着滚动高度的逐渐增加,数据也会以逐页底部加载的形式逐渐增加。列表页浏览到某个位置,用户看到感兴趣的项目,点击查看其详情,进入详情页。从详情页返回列表页时,离开列表页时需要停留在浏览位置。未提交的表单、管理系统中可切换可关闭的功能标签等。这类数据随着用户交互逐渐变化或增长,在这里理解为状态。在交互过程中,由于某些原因需要暂时离开交互场景,需要在React中保存状态。我们通常使用路由来管理不同的页面。切换页面时,路由会卸载不匹配的页面组件。因此,在上面的列表页示例中,当用户从详情页选择返回列表页时,会返回到列表页的顶部,因为列表页组件被路由卸载后重新构建,并且状态丢失了。如何在Vue中保存React中的状态,我们可以很方便的通过[1]标签实现状态保存。此标记缓存非活动组件实例而不是销毁它们。React中没有这样的功能。官方曾有人提到过函数issues[2],但官方认为这个函数容易造成记忆。泄露了,说明暂时不考虑支持,所以需要找一个通用的解决方案:ManuallysavethestateManuallysavethestate是一个比较通用的方案,可以配合React组件的componentWillUnmount生命周期来传递状态管理层如redux通过componentDidMount循环保存数据和恢复数据。当需要保存的状态较少时,这种方式可以比较快速的实现我们需要的功能,但是当数据量较大或者情况发生变化时,手动保存的状态就会发生变化。作为程序员,当然是越懒越好。为了不用担心每次如何保存和恢复数据,我们需要研究如何通过路由自动保存状态来实现状态自动保存(通常使用react-router)由于React中状态的丢失是由卸载引起的路由切换时的组件,可以尝试从路由机制入手,改变路由组件的渲染行为。我们有以下几种方式来实现这个功能1.重写组件,请参考react-live-route[4]重写可以实现我们想要的功能,但是成本比较高,需要注意的是原创功能保存,并兼容多个react-router版本2.将路由库替换为react-keeper[5]完全替换路由方案是一件有风险的事情,需要慎重考虑3.3.基于component扩展已有的行为,可以参考react-router-cache-route[6]看了的源码,发现如果使用component或者render属性,route的去向无法避免不匹配时被卸载。使用children属性作为方法,我们有可能手动控制渲染行为,关键代码在这里https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Route.js#L41-L72//摘自Route组件中的render函数realTherenderingresultofif(children===undefined){...children=null;}}return({children&&!isEmptyChildren(children)//当children存在时,children会被用于渲染?children:props.match?component?React.createElement(component,props):render?render(props):null//使用render属性不能阻止组件的卸载:null//使用组件属性不能阻止组件的卸载);基于以上源码探索,我们可以扩展,将的不匹配行为从卸载调整为隐藏,如下{props=>(

)}上面是最简单的调整方法,在实际情况中,还需要考虑隐藏状态下match为null导致组件报错的问题,并且因为不再是组件卸载,所以不配合与TransitionGroup配合得很好,这使得过渡动画难以实现。使用react-router-cache-route[7],得到的效果大致如下图所示。上面探讨了通过路由自动保存状态的可能性,以及现有的实现,但毕竟不是真正纯粹的KeepAlive功能。接下来,我们尝试探索真实的KeepAlive函数的实现,模拟真实的函数。以下是预期的用法show&&()}
)}实现原理说起来比较简单,因为React会卸载固有组件层级中的组件,所以我们需要将组件提取出来,也就是它的children属性,将其渲染成一个不会被卸载的组件,从而实现这个功能。下面是react-activation[8][9]实现效果的在线示例,在实际实现过程中遇到了很多问题,都是打破了React原有的层次结构造成的,比如渲染延迟Provider上下文函数失败错误边界失败React.Suspense和React.lazy失败React合成事件冒泡失败10]结论状态缓存是应用程序中非常普遍的需求。当需要处理的数据量较小时,大部分问题都可以使用手动状态缓存来解决。但是,当情况比较复杂的时候,就需要尝试单独解决缓存功能了,为了更好的分离业务开发过程中的关注点。目前的实现都有自己的问题,但探索的过程还是很有趣的。最好的办法还是官方支持,但目前还不能报太多期望