網站首頁 編程語言 正文
背景
由于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
import React, { useReducer, useCallback } from "react";
import CacheContext from "./CacheContext";
import cacheReducer from "./cacheReducer";
import * as cacheTypes from "./cache-types";
function KeepAliveProvider(props) {
let [cacheStates, dispatch] = useReducer(cacheReducer, {});
const mount = useCallback(
({ cacheId, element }) => {
// 掛載元素方法,提供子組件調用掛載元素
if (cacheStates[cacheId]) {
let cacheState = cacheStates[cacheId];
if (cacheState.status === cacheTypes.DESTROY) {
let doms = cacheState.doms;
doms.forEach((dom) => dom.parentNode.removeChild(dom));
dispatch({ type: cacheTypes.CREATE, payload: { cacheId, element } }); // 創建緩存
}
} else {
dispatch({ type: cacheTypes.CREATE, payload: { cacheId, element } }); // 創建緩存
}
},
[cacheStates]
);
let handleScroll = useCallback(
// 緩存滾動條
(cacheId, { target }) => {
if (cacheStates[cacheId]) {
let scrolls = cacheStates[cacheId].scrolls;
scrolls[target] = target.scrollTop;
}
},
[cacheStates]
);
return (
<CacheContext.Provider
value={{ mount, cacheStates, dispatch, handleScroll }}
>
{props.children}
{/* cacheStates維護所有緩存信息, dispatch派發修改緩存狀態*/}
{Object.values(cacheStates)
.filter((cacheState) => cacheState.status !== cacheTypes.DESTROY)
.map(({ cacheId, element }) => (
<div
id={`cache_${cacheId}`}
key={cacheId}
// 原生div中聲明ref,當div渲染到頁面,會執行ref中的回調函數,這里在id為cache_${cacheId}的div渲染完成后,會繼續渲染子元素
ref={(dom) => {
let cacheState = cacheStates[cacheId];
if (
dom &&
(!cacheState.doms || cacheState.status === cacheTypes.DESTROY)
) {
let doms = Array.from(dom.childNodes);
dispatch({
type: cacheTypes.CREATED,
payload: { cacheId, doms },
});
}
}}
>
{element}
</div>
))}
</CacheContext.Provider>
);
}
const useCacheContext = () => {
const context = React.useContext(CacheContext);
if (!context) {
throw new Error("useCacheContext必須在Provider中使用");
}
return context;
};
export { KeepAliveProvider, useCacheContext };
withKeepAlive.js
import React, { useContext, useRef, useEffect } from "react";
import CacheContext from "./CacheContext";
import * as cacheTypes from "./cache-types";
function withKeepAlive(
OldComponent,
{ cacheId = window.location.pathname, scroll = false }
) {
return function (props) {
const { mount, cacheStates, dispatch, handleScroll } =
useContext(CacheContext);
const ref = useRef(null);
useEffect(() => {
if (scroll) {
// scroll = true, 監聽緩存組件的滾動事件,調用handleScroll()緩存滾動條
ref.current.addEventListener(
"scroll",
handleScroll.bind(null, cacheId),
true
);
}
}, [handleScroll]);
useEffect(() => {
let cacheState = cacheStates[cacheId];
if (
cacheState &&
cacheState.doms &&
cacheState.status !== cacheTypes.DESTROY
) {
// 如果真實dom已經存在,且狀態不是DESTROY,則用當前的真實dom
let doms = cacheState.doms;
doms.forEach((dom) => ref.current.appendChild(dom));
if (scroll) {
// 如果scroll = true, 則將緩存中的scrollTop拿出來賦值給當前dom
doms.forEach((dom) => {
if (cacheState.scrolls[dom])
dom.scrollTop = cacheState.scrolls[dom];
});
}
} else {
// 如果還沒產生真實dom,派發生成
mount({
cacheId,
element: <OldComponent {...props} dispatch={dispatch} />,
});
}
}, [cacheStates, dispatch, mount, props]);
return <div id={`keepalive_${cacheId}`} ref={ref} />;
};
}
export default withKeepAlive;
index.js
export { KeepAliveProvider } from "./KeepAliveProvider";
export {default as withKeepAlive} from './withKeepAlive';
使用:
1.用<KeepAliveProvider></KeepAliveProvider>將目標緩存組件或者父級包裹;
2.將需要緩存的組件,傳入withKeepAlive方法中,該方法返回一個緩存組件;
3.使用該組件;
App.js
import React from "react";
import {
BrowserRouter,
Link,
Route,
Switch,
} from "react-router-dom";
import Home from "./Home.js";
import List from "./List.js";
import Detail from "./Detail.js";
import { KeepAliveProvider, withKeepAlive } from "./keepalive-cpn";
const KeepAliveList = withKeepAlive(List, { cacheId: "list", scroll: true });
function App() {
return (
<KeepAliveProvider>
<BrowserRouter>
<ul>
<li>
<Link to="/">首頁</Link>
</li>
<li>
<Link to="/list">列表頁</Link>
</li>
<li>
<Link to="/detail">詳情頁A</Link>
</li>
</ul>
<Switch>
<Route path="/" component={Home} exact></Route>
<Route path="/list" component={KeepAliveList}></Route>
<Route path="/detail" component={Detail}></Route>
</Switch>
</BrowserRouter>
</KeepAliveProvider>
);
}
export default App;
效果:
假設有個需求,從首頁到列表頁,需要清空搜索條件,重新請求數據,即回到首頁,需要清除列表頁的緩存。
上面的KeepAliveProvider.js中,暴露了一個useCacheContext()的hook,該hook返回了緩存組件相關數據和方法,這里可以用于清除緩存:
Home.js
import React, { useEffect } from "react";
import { DESTROY } from "./keepalive-cpn/cache-types";
import { useCacheContext } from "./keepalive-cpn/KeepAliveProvider";
const Home = () => {
const { cacheStates, dispatch } = useCacheContext();
const clearCache = () => {
if (cacheStates && dispatch) {
for (let key in cacheStates) {
if (key === "list") {
dispatch({ type: DESTROY, payload: { cacheId: key } });
}
}
}
};
useEffect(() => {
clearCache();
// eslint-disable-next-line
}, []);
return (
<div>
<div>首頁</div>
</div>
);
};
export default Home;
效果:
至此,react簡易版的keepalive組件已經完成啦~
原文鏈接:https://www.cnblogs.com/coder--wang/p/17049408.html
相關推薦
- 2022-10-03 利用正則表達式校驗金額最多保留兩位小數實例代碼_正則表達式
- 2022-09-17 python面試題之read、readline和readlines的區別詳解_python
- 2023-10-15 小程序動態隱藏分享按鈕
- 2022-08-10 C++示例講解friend?static?const關鍵字的用法_C 語言
- 2022-04-07 Swift實現簡易計算器功能_Swift
- 2022-07-23 Python實現單向鏈表_python
- 2023-11-15 Latex文獻報錯 Something‘s wrong--perhaps a missing \it
- 2022-08-10 C#中通過Command模式實現Redo/Undo方案_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支