React中實現(xiàn)keepalive組件緩存效果的方法詳解
背景
由于react官方并沒有提供緩存組件相關的api(類似vue中的keepalive),在某些場景,會使得頁面交互性變的很差,比如在有搜索條件的表格頁面,點擊某一條數(shù)據跳轉到詳情頁面,再返回表格頁面,會重新請求數(shù)據,搜索條件也將清空,用戶得重新輸入搜索條件,再次請求數(shù)據,大大降低辦公效率,如圖:

目標:封裝keepalive緩存組件,實現(xiàn)組件的緩存,并暴露相關方法,可以手動清除緩存。
版本:React 17,react-router-dom 5
結構

代碼
cache-types.js
// 緩存狀態(tài) export const CREATE = 'CREATE'; // 創(chuàng)建 export const CREATED = 'CREATED'; // 創(chuàng)建成功 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 } }); // 創(chuàng)建緩存
}
} else {
dispatch({ type: cacheTypes.CREATE, payload: { cacheId, element } }); // 創(chuàng)建緩存
}
},
[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派發(fā)修改緩存狀態(tài)*/}
{Object.values(cacheStates)
.filter((cacheState) => cacheState.status !== cacheTypes.DESTROY)
.map(({ cacheId, element }) => (
<div
id={`cache_${cacheId}`}
key={cacheId}
// 原生div中聲明ref,當div渲染到頁面,會執(zhí)行ref中的回調函數(shù),這里在id為cache_${cacheId}的div渲染完成后,會繼續(xù)渲染子元素
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, 監(jiān)聽緩存組件的滾動事件,調用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已經存在,且狀態(tài)不是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,派發(fā)生成
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;效果:

假設有個需求,從首頁到列表頁,需要清空搜索條件,重新請求數(shù)據,即回到首頁,需要清除列表頁的緩存。
上面的KeepAliveProvider.js中,暴露了一個useCacheContext()的hook,該hook返回了緩存組件相關數(shù)據和方法,這里可以用于清除緩存:
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組件已經完成啦~
到此這篇關于React中實現(xiàn)keepalive組件緩存效果的方法詳解的文章就介紹到這了,更多相關React keepalive組件緩存效果內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
ahooks封裝cookie?localStorage?sessionStorage方法
這篇文章主要為大家介紹了ahooks封裝cookie?localStorage?sessionStorage的方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07

