React中實現(xiàn)keepalive組件緩存效果的方法詳解
背景
由于react官方并沒有提供緩存組件相關的api(類似vue中的keepalive),在某些場景,會使得頁面交互性變的很差,比如在有搜索條件的表格頁面,點擊某一條數(shù)據(jù)跳轉(zhuǎn)到詳情頁面,再返回表格頁面,會重新請求數(shù)據(jù),搜索條件也將清空,用戶得重新輸入搜索條件,再次請求數(shù)據(jù),大大降低辦公效率,如圖:
目標:封裝keepalive緩存組件,實現(xiàn)組件的緩存,并暴露相關方法,可以手動清除緩存。
版本:React 17,react-router-dom 5
結(jié)構(gòu)
代碼
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 }) => { // 掛載元素方法,提供子組件調(diào)用掛載元素 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中的回調(diào)函數(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)聽緩存組件的滾動事件,調(diào)用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已經(jīng)存在,且狀態(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 { // 如果還沒產(chǎn)生真實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ù)據(jù),即回到首頁,需要清除列表頁的緩存。
上面的KeepAliveProvider.js中,暴露了一個useCacheContext()的hook,該hook返回了緩存組件相關數(shù)據(jù)和方法,這里可以用于清除緩存:
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組件已經(jīng)完成啦~
到此這篇關于React中實現(xiàn)keepalive組件緩存效果的方法詳解的文章就介紹到這了,更多相關React keepalive組件緩存效果內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
React中getDefaultProps的使用小結(jié)
React中的getDefaultProps功能允許開發(fā)者為類組件定義默認屬性,提高組件的靈活性和容錯性,本文介紹了getDefaultProps的作用、語法以及最佳實踐,并探討了其他替代方案,如函數(shù)組件中的默認參數(shù)、高階組件和ContextAPI等,理解這些概念有助于提升代碼的可維護性和用戶體驗2024-09-09ahooks封裝cookie?localStorage?sessionStorage方法
這篇文章主要為大家介紹了ahooks封裝cookie?localStorage?sessionStorage的方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07