首页 > 其他分享 >React中实现keepalive组件缓存效果

React中实现keepalive组件缓存效果

时间:2023-01-13 16:44:10浏览次数:49  
标签:缓存 dom cacheId cacheState React cacheStates import keepalive

背景:由于react官方并没有提供缓存组件相关的api(类似vue中的keepalive),在某些场景,会使得页面交互性变的很差,比如在有搜索条件的表格页面,点击某一条数据跳转到详情页面,再返回表格页面,会重新请求数据,搜索条件也将清空,用户得重新输入搜索条件,再次请求数据,大大降低办公效率,如图:

 

 

 

目标:封装keepalive缓存组件,实现组件的缓存,并暴露相关方法,可以手动清除缓存。

版本:React 17,react-router-dom 5

结构

代码:

cache-types.js

// 缓存状态
export const CREATE = 'CREATE';        // 创建
export const CREATED = 'CREATED';      // 创建成功
export const ACTIVE = 'ACTIVE';        // 激活
export const DESTROY = 'DESTROY';      // 销毁

CacheContext.js

import React from 'react';
const CacheContext = React.createContext();
export default CacheContext;

KeepAliveProvider.js

 1 import React, { useReducer, useCallback } from "react";
 2 import CacheContext from "./CacheContext";
 3 import cacheReducer from "./cacheReducer";
 4 import * as cacheTypes from "./cache-types";
 5 function KeepAliveProvider(props) {
 6   let [cacheStates, dispatch] = useReducer(cacheReducer, {});
 7   const mount = useCallback(
 8     ({ cacheId, element }) => {
 9       // 挂载元素方法,提供子组件调用挂载元素
10       if (cacheStates[cacheId]) {
11         let cacheState = cacheStates[cacheId];
12         if (cacheState.status === cacheTypes.DESTROY) {
13           let doms = cacheState.doms;
14           doms.forEach((dom) => dom.parentNode.removeChild(dom));
15           dispatch({ type: cacheTypes.CREATE, payload: { cacheId, element } }); // 创建缓存
16         }
17       } else {
18         dispatch({ type: cacheTypes.CREATE, payload: { cacheId, element } }); // 创建缓存
19       }
20     },
21     [cacheStates]
22   );
23   let handleScroll = useCallback(
24     // 缓存滚动条
25     (cacheId, { target }) => {
26       if (cacheStates[cacheId]) {
27         let scrolls = cacheStates[cacheId].scrolls;
28         scrolls[target] = target.scrollTop;
29       }
30     },
31     [cacheStates]
32   );
33   return (
34     <CacheContext.Provider
35       value={{ mount, cacheStates, dispatch, handleScroll }}
36     >
37       {props.children}
38       {/* cacheStates维护所有缓存信息, dispatch派发修改缓存状态*/}
39       {Object.values(cacheStates)
40         .filter((cacheState) => cacheState.status !== cacheTypes.DESTROY)
41         .map(({ cacheId, element }) => (
42           <div
43             id={`cache_${cacheId}`}
44             key={cacheId}
45             // 原生div中声明ref,当div渲染到页面,会执行ref中的回调函数,这里在id为cache_${cacheId}的div渲染完成后,会继续渲染子元素
46             ref={(dom) => {
47               let cacheState = cacheStates[cacheId];
48               if (
49                 dom &&
50                 (!cacheState.doms || cacheState.status === cacheTypes.DESTROY)
51               ) {
52                 let doms = Array.from(dom.childNodes);
53                 dispatch({
54                   type: cacheTypes.CREATED,
55                   payload: { cacheId, doms },
56                 });
57               }
58             }}
59           >
60             {element}
61           </div>
62         ))}
63     </CacheContext.Provider>
64   );
65 }
66 const useCacheContext = () => {
67   const context = React.useContext(CacheContext);
68   if (!context) {
69     throw new Error("useCacheContext必须在Provider中使用");
70   }
71   return context;
72 };
73 export { KeepAliveProvider, useCacheContext };

withKeepAlive.js

 1 import React, { useContext, useRef, useEffect } from "react";
 2 import CacheContext from "./CacheContext";
 3 import * as cacheTypes from "./cache-types";
 4 function withKeepAlive(
 5   OldComponent,
 6   { cacheId = window.location.pathname, scroll = false }
 7 ) {
 8   return function (props) {
 9     const { mount, cacheStates, dispatch, handleScroll } =
10       useContext(CacheContext);
11     const ref = useRef(null);
12     useEffect(() => {
13       if (scroll) {
14         // scroll = true, 监听缓存组件的滚动事件,调用handleScroll()缓存滚动条
15         ref.current.addEventListener(
16           "scroll",
17           handleScroll.bind(null, cacheId),
18           true
19         );
20       }
21     }, [handleScroll]);
22     useEffect(() => {
23       let cacheState = cacheStates[cacheId];
24       if (
25         cacheState &&
26         cacheState.doms &&
27         cacheState.status !== cacheTypes.DESTROY
28       ) {
29         // 如果真实dom已经存在,且状态不是DESTROY,则用当前的真实dom
30         let doms = cacheState.doms;
31         doms.forEach((dom) => ref.current.appendChild(dom));
32         if (scroll) {
33           // 如果scroll = true, 则将缓存中的scrollTop拿出来赋值给当前dom
34           doms.forEach((dom) => {
35             if (cacheState.scrolls[dom])
36               dom.scrollTop = cacheState.scrolls[dom];
37           });
38         }
39       } else {
40         // 如果还没产生真实dom,派发生成
41         mount({
42           cacheId,
43           element: <OldComponent {...props} dispatch={dispatch} />,
44         });
45       }
46     }, [cacheStates, dispatch, mount, props]);
47     return <div id={`keepalive_${cacheId}`} ref={ref} />;
48   };
49 }
50 export default withKeepAlive;

index.js

export { KeepAliveProvider } from "./KeepAliveProvider";
export {default as withKeepAlive} from './withKeepAlive';

使用

  1.用<KeepAliveProvider></KeepAliveProvider>将目标缓存组件或者父级包裹;

  2.将需要缓存的组件,传入withKeepAlive方法中,该方法返回一个缓存组件;

  3.使用该组件;

App.js

 1 import React from "react";
 2 import {
 3   BrowserRouter,
 4   Link,
 5   Route,
 6   Switch,
 7 } from "react-router-dom";
 8 import Home from "./Home.js";
 9 import List from "./List.js";
10 import Detail from "./Detail.js";
11 import { KeepAliveProvider, withKeepAlive } from "./keepalive-cpn";
12 
13 const KeepAliveList = withKeepAlive(List, { cacheId: "list", scroll: true });
14 
15 function App() {
16   return (
17     <KeepAliveProvider>
18       <BrowserRouter>
19         <ul>
20           <li>
21             <Link to="/">首页</Link>
22           </li>
23           <li>
24             <Link to="/list">列表页</Link>
25           </li>
26           <li>
27             <Link to="/detail">详情页A</Link>
28           </li>
29         </ul>
30         <Switch>
31           <Route path="/" component={Home} exact></Route>
32           <Route path="/list" component={KeepAliveList}></Route>
33           <Route path="/detail" component={Detail}></Route>
34         </Switch>
35       </BrowserRouter>
36     </KeepAliveProvider>
37   );
38 }
39 
40 export default App;

效果

假设有个需求,从首页到列表页,需要清空搜索条件,重新请求数据,即回到首页,需要清除列表页的缓存。

上面的KeepAliveProvider.js中,暴露了一个useCacheContext()的hook,该hook返回了缓存组件相关数据和方法,这里可以用于清除缓存:

Home.js

 1 import React, { useEffect } from "react";
 2 import { DESTROY } from "./keepalive-cpn/cache-types";
 3 import { useCacheContext } from "./keepalive-cpn/KeepAliveProvider";
 4 
 5 const Home = () => {
 6   const { cacheStates, dispatch } = useCacheContext();
 7 
 8   const clearCache = () => {
 9     if (cacheStates && dispatch) {
10       for (let key in cacheStates) {
11         if (key === "list") {
12           dispatch({ type: DESTROY, payload: { cacheId: key } });
13         }
14       }
15     }
16   };
17   useEffect(() => {
18     clearCache();
19     // eslint-disable-next-line
20   }, []);
21   return (
22     <div>
23       <div>首页</div>
24     </div>
25   );
26 };
27 
28 export default Home;

效果

至此,react简易版的keepalive组件已经完成啦~

 

脚踏实地行,海阔天空飞

 

标签:缓存,dom,cacheId,cacheState,React,cacheStates,import,keepalive
From: https://www.cnblogs.com/coder--wang/p/17049408.html

相关文章

  • 跳过设置!直接使用KendoReact模板创建React应用程序
    KendoUI致力于新的开发,来满足不断变化的需求。现在我们非常自豪地宣布,通过React框架的KendoUIJavaScript封装来支持ReactJavascript框架。KendoReact能够为客户提供更......
  • JAVA中DNS缓存设置(转)
    原文:https://blog.csdn.net/guanfengliang1988/article/details/92813431作者:夜风_BLOG我们上网的原点就是打开浏览器,在上方地址栏输入网址的那一刻,这个回车按了之后,发生......
  • react中二维码生成
    参考文档:https://blog.csdn.net/weixin_45022563/article/details/124843593 awesomeqr/react案例:注意需要给父级div设置高宽下载:yarnadd@awesomeqr/react......
  • React Context 详细介绍(状态共享、数据传递)
    Context是什么?Context提供了一个无需为每层组件手动添加props,就能在组件树间进行数据传递的方法。在一个典型的React应用中,数据是通过props属性自上而下(由父及子)进......
  • 学习笔记——Mybatis中缓存机制
    2023-01-12一、Mybatis中缓存机制1、一级缓存(1)概述:一级缓存(即本地缓存或SqlSession级别缓存)(2)特点:①一级缓存默认开启②不能关闭③可以清空(3)缓存原理①当第一次获......
  • React面试宝典
    React面试宝典一、组件基础梳理组件设计原理与思路分析和解决问题的技巧1.React原理......
  • mybatis 一级、二级缓存机制
     MyBatis提供了对缓存的支持,分为一级缓存和二级缓存  一级缓存是SqlSession级别的缓存。在操作数据库时需要构造SqlSession对象,在对象中有一个数据结构(HashMap......
  • 【一句话】Redis的3中缓存策略
    首先一句话:旁路缓存模式策略:写->写DB,删缓存,读->读cache,没有则读DB,然后更新到缓存读写穿透策略:写->写缓存,然后由缓存系统写DB,读->读cache,没有则将DB写入cache,再读cache异......
  • .net core(.net 6) IResourceFilter 扩展缓存
    参考:.netcore(.net6)IResourceFilter的基础使用(IAsyncResourceFilter) 缓存:就是一个临时存储区域,以一个Key-value格式保存数据;key---保存数据的标识,也需要这个表示key......
  • keepalived实现双机热备 | 技术
    Keepalived简介Keepalived是Linux下一个轻量级别的高可用解决方案。高可用(HighAvalilability,HA),其实两种不同的含义:广义来讲,是指整个系统的高可用行,狭义的来讲就是之主机......