怎么使用react-activation實現keepAlive支持返回傳參
這篇文章主要介紹“怎么使用react-activation實現keepAlive支持返回傳參”,在日常操作中,相信很多人在怎么使用react-activation實現keepAlive支持返回傳參問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么使用react-activation實現keepAlive支持返回傳參”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
介紹
這個項目是一個商城的后臺管理系統,用umi2.0搭建,狀態管理使用dva,想要實現類似vue keep-alive的效果。
具體表現為:
從列表頁A跳轉A的詳情頁,列表頁A緩存
詳情頁沒做任何操作,跳回列表頁A,列表頁A不刷新,列表頁A頁碼不變
詳情頁進行了編輯操作,跳回列表頁A,列表頁A刷新,列表頁A頁碼不變
詳情頁進行了新建操作,跳回列表頁A,列表頁A刷新,列表頁A頁碼變為1
從列表頁A跳轉列表頁B,列表頁A不緩存
總結就是,一個頁面只有跳轉指定頁面的時候才緩存,并且當返回這個被緩存的頁面時,可以控制是否刷新。
代碼
1、安裝react-activation
"react-activation":?"^0.10.2",
2、給路由增加meta
這個項目使用的是集中式配置路由,我增加了meta屬性,meta.keepAlive存在表示這是一個需要被keepAlive的路由,meta.keepAlive.toPath表示只有當前往這個路由的時候,需要緩存
const?routes?=?[ ????... ????{ ????????name:?'商品管理(商城商品)',? ????????path:?'/web/supplier/goods/mallgoodsmgr', ????????component:?'./supplier/goods/goodsManage', ????????meta:?{ ??????????keepAlive:?{ ????????????toPath:?'/web/supplier/goods/mallgoodsmgr/detail',?//?只有去詳情頁的時候?才需要緩存?商品管理(商城商品)這個路由 ??????????}, ????????}, ????} ????... ]
3、根組件中渲染
在根組件中,用<AliveScope/>包裹整個應用,用<KeepAlive/>包裹需要緩存的頁面。文檔中這部分寫在<App/>中,如果是umi可以寫在layouts里。
通過tree的扁平化計算獲取全部的帶有meta.keepAlive的routes:keepAliveRoutes,通過location.pathname判斷,如果當前頁面是需要keepAlive的,那么就需要用<KeepAlive/>包裹。
import?KeepAlive,?{?AliveScope,?useAliveController?}?from?'react-activation' //?tree扁平化 function?treeToList(tree,?childrenKey?=?'routes')?{ ??var?queen?=?[] ??var?out?=?[] ??queen?=?queen.concat(tree) ??while?(queen.length)?{ ????var?first?=?queen.shift() ????if?(first[childrenKey])?{ ??????queen?=?queen.concat(first[childrenKey]) ??????delete?first[childrenKey] ????} ????out.push(first) ??} ??return?out } //?從routes路由tree里,拿到所有meta.keepAlive的路由:keepAliveRoutes const?allFlatRoutes?=?treeToList(routes)?//?所有路由 const?keepAliveRoutes?=?allFlatRoutes.filter((item)?=>?item.meta?.keepAlive)?//?keepAlive的路由 function?Index(props)?{ ??const?location?=?useLocation() ?? ??const?routeItem?=?keepAliveRoutes.find( ????(item)?=>?item.path?==?location.pathname ??)?//?from?頁面 ??let?dom?=?props.children ??if?(routeItem)?{ ????dom?=?<KeepAlive?id={location.pathname}>{props.children}</KeepAlive>?//?id?一定要加?否則?keepAlive的頁面?跳轉?另一個keepAlive的頁面?會有問題 ??} ??return?( ????<AliveScope> ??????<div?className={styles.page_container}>{dom}</div> ????</AliveScope> ??) }
注意AliveScope中包含多個KeepAlive的話,<KeepAlive/>一定要帶id。
4、跳轉指定頁面的時候才緩存
上一步之后,頁面雖然被緩存,但是它跳轉任何頁面都會緩存,我們需要只有跳轉指定頁面的時候才緩存。
我的方法是
如果跳轉的頁面正好是它自己的meta.keepAlive.toPath,那就不做任何操作(因為此時本頁面已經被KeepAlive包裹了,處于緩存的狀態)
如果不是它自己的meta.keepAlive.toPath,調用clear方法,清空緩存
4.1 clear方法
react-activation提供useAliveController可以手動控制緩存,其中clear方法用于清空所有緩存中的 KeepAlive
4.2 用狀態管理記錄toPath
監聽history,用狀態管理(我用的dva)記錄即將前往的頁面(下一個頁面)toPath
我通過dva記錄應用即將前往的頁面
const?GlobalModel?=?{ ??namespace:?'global', ??state:?{ ????/** ?????*?keepAlive ?????*/ ????toPath:?'', ????keepAliveOptions:?{},?//?給keepAlive的頁面?傳的options ??}, ??effects:?{}, ??reducers:?{ ????save(state,?{?payload?})?{ ??????return?{ ????????...state, ????????...payload, ??????} ????}, ????setToPath(state,?{?payload?})?{ ??????return?{ ????????...state, ????????toPath:?payload, ??????} ????}, ??}, ??subscriptions:?{ ????setup({?history,?dispatch?})?{ ??????//?Subscribe?history(url)?change,?trigger?`load`?action?if?pathname?is?`/` ??????history.listen((route,?typeStr)?=>?{ ????????const?{?pathname?}?=?route ????????dispatch({ ??????????type:?'setToPath', ??????????payload:?pathname, ????????}) ??????}) ????}, ??}, }
4.3 給根組件增加useEffect
根組件從dva中讀取即將訪問的頁面toPath,然后加一個useEffect,如果即將前往的頁面不是當前路由自己的meta.keepAlive.toPath,就執行react-activation提供的clear方法
... function?Index(props)?{ ??const?location?=?useLocation() ??const?toPath?=?props.global.toPath?//?從dva中拿到?將要訪問的頁面 ?? ??const?routeItem?=?keepAliveRoutes.find( ????(item)?=>?item.path?==?location.pathname ??)?//?from?頁面 ?? ?? ??///?新加代碼 ??///?新加代碼 ??///?新加代碼 ??useEffect(()?=>?{ ????console.log('toPath改變',?toPath) ????//?from頁面?是需要keepAlive的頁面 ????if?(routeItem)?{ ??????console.log('from頁面?是需要keepAlive的頁面',?routeItem) ??????if?(toPath?==?routeItem.meta?.keepAlive.toPath)?{ ????????//?所去的?頁面?正好是當前這個路由的?keepAlive.toPath ????????console.log('所去的?頁面?正好是當前這個路由的?keepAlive.toPath,不做什么') ??????}?else?{ ????????console.log('clear') ????????if?(aliveController?.clear)?{ ??????????aliveController.clear() ????????} ??????} ????} ??},?[toPath]) ??///?新加代碼?end ??let?dom?=?props.children ??if?(routeItem)?{ ????dom?=?<KeepAlive?id={location.pathname}>{props.children}</KeepAlive>?//?id?一定要加?否則?keepAlive的頁面?跳轉?另一個keepAlive的頁面?會有問題 ??} ??return?( ????<AliveScope> ??????<div?className={styles.page_container}>{dom}</div> ????</AliveScope> ??) } export?default?connect(({?global,?login?})?=>?({?global,?login?}))(Index)
4.4 優化
現在有一個問題:從列表A跳轉詳情頁,然后跳轉列表B,再跳轉列表A的時候,A是不刷新的:
列表A => 詳情頁 => 列表B => 列表A 此時列表A不刷新或者空白。
因為從詳情頁出來(跳轉列表B)的時候,我們沒有清空列表A的緩存。
所以要檢查當前頁面是否是某個需要keepAlive頁面的toPath頁面
根組件:
function?Index(){ ??... ?? ??const?parentItem?=?keepAliveRoutes.find((item)?=>?item.meta?.keepAlive?.toPath?==?location.pathname)?//?parentItem存在表示?當前頁面?是某個keepAlive的頁面?的toPath ??useEffect(()?=>?{ ????console.log('toPath改變',?toPath) ????... ???? ????///?新加代碼 ????///?新加代碼 ????///?新加代碼 ????//?from頁面?是某個keepAlive的頁面?的toPath ????if?(parentItem)?{ ??????console.log('from頁面?是某個keepAlive的頁面?的toPath,parentItem',?parentItem) ??????if?(toPath?==?parentItem.path)?{ ????????//?所去的?頁面是?parentItem.path ????????console.log('所去的?頁面是?parentItem.path,不做什么') ??????}?else?{ ????????console.log('clear') ????????if?(aliveController?.clear)?{ ??????????aliveController.clear() ????????} ??????} ????} ??},?[toPath]) ?? ??... }
5、抽離邏輯到自定義hooks
useKeepAliveLayout.js
import?{?useEffect?}?from?'react' import?{?useLocation?}?from?'react-router-dom' import?KeepAlive,?{?AliveScope,?useAliveController?}?from?'react-activation' import?routes?from?'../../config/router.config' //?tree扁平化 function?treeToList(tree,?childrenKey?=?'routes')?{ ??var?queen?=?[] ??var?out?=?[] ??queen?=?queen.concat(tree) ??while?(queen.length)?{ ????var?first?=?queen.shift() ????if?(first[childrenKey])?{ ??????queen?=?queen.concat(first[childrenKey]) ??????delete?first[childrenKey] ????} ????out.push(first) ??} ??return?out } const?allFlatRoutes?=?treeToList(routes)?//?所有路由 const?keepAliveRoutes?=?allFlatRoutes.filter((item)?=>?item.meta?.keepAlive)?//?keepAlive的路由 function?index(props)?{ ??const?location?=?useLocation() ??//?keep?alive ??const?aliveController?=?useAliveController() ??const?toPath?=?props.global.toPath?//?將要訪問的頁面 ??const?routeItem?=?keepAliveRoutes.find((item)?=>?item.path?==?location.pathname)?//?from?頁面 ??const?parentItem?=?keepAliveRoutes.find((item)?=>?item.meta?.keepAlive?.toPath?==?location.pathname) ??useEffect(()?=>?{ ????console.log('toPath改變',?toPath) ????//?from頁面?是需要keepAlive的頁面 ????if?(routeItem)?{ ??????console.log('from頁面?是需要keepAlive的頁面',?routeItem) ??????if?(toPath?==?routeItem.meta?.keepAlive.toPath)?{ ????????//?所去的?頁面?正好是當前這個路由的?keepAlive.toPath ????????console.log('所去的?頁面?正好是當前這個路由的?keepAlive.toPath,不做什么') ??????}?else?{ ????????console.log('clear') ????????if?(aliveController?.clear)?{ ??????????aliveController.clear() ????????} ??????} ????} ????//?from頁面?是某個keepAlive的頁面?的toPath ????if?(parentItem)?{ ??????console.log('from頁面?是某個keepAlive的頁面?的toPath,parentItem',?parentItem) ??????if?(toPath?==?parentItem.path)?{ ????????//?所去的?頁面是?parentItem.path ????????console.log('所去的?頁面是?parentItem.path,不做什么') ??????}?else?{ ????????console.log('clear') ????????if?(aliveController?.clear)?{ ??????????aliveController.clear() ????????} ??????} ????} ??},?[toPath]) ??return?{ ????fromIsNeedKeepAlive:?routeItem, ??} } export?default?index
根組件只需要引入這個hooks就可以了:
function?Index(props)?{ ??const?location?=?useLocation() ??const?{?fromIsNeedKeepAlive?}?=?useKeepAliveLayout(props)?//?關鍵代碼關鍵代碼關鍵代碼 ??let?dom?=?props.children ??if?(fromIsNeedKeepAlive)?{ ????dom?=?<KeepAlive?id={location.pathname}>{props.children}</KeepAlive>?//?id?一定要加?否則?keepAlive的頁面?跳轉?另一個keepAlive的頁面?會有問題 ??} ??return?( ????<AliveScope> ??????<div?className={styles.page_container}>{dom}</div> ????</AliveScope> ??) }
6、 從詳情頁返回列表頁的時候,控制列表頁是否刷新,即返回傳參
現在只剩下這最后一個問題了,其實就是keepAlive的頁面,goBack傳參的問題
思路:
狀態管理中增加一個keepAliveOptions對象,這就是詳情頁給列表頁傳的參數
詳情頁執行goBack的時候,調用狀態管理dispatch修改keepAliveOptions
列表頁監聽keepAliveOptions,如果keepAliveOptions改變就執行傳入的方法
useKeepAliveOptions.js
import?{?useEffect?}?from?'react' import?{?useDispatch,?useStore?}?from?'dva' import?{?router?}?from?'umi' /** ?*?@description?keepAlive的頁面,當有參數傳過來的時候,可以用這個監聽到 ?*?@param?{(options:object)=>void}?func ?*/ export?function?useKeepAlivePageShow(func)?{ ??const?dispatch?=?useDispatch() ??const?store?=?useStore() ??const?state?=?store.getState() ??const?options?=?state.global.keepAliveOptions????{} ??useEffect(()?=>?{ ????func(options)?//?執行 ????return?()?=>?{ ??????console.log('keepAlive頁面?的緩存?卸載') ??????dispatch({ ????????type:?'global/save', ????????payload:?{ ??????????keepAliveOptions:?{}, ????????}, ??????}) ????} ??},?[JSON.stringify(options)]) } /** ?*?@description?PageA(keepAlive的頁面)去了?PageB,?當從PageB?goBack,想要給PageA傳參的時候,需要使用這個方法 ?*?@returns?{(params:object)=>void} ?*/ export?function?useKeepAliveGoback()?{ ??const?dispatch?=?useDispatch() ??function?goBack(parmas?=?{})?{ ????dispatch({ ??????type:?'global/save', ??????payload:?{ ????????keepAliveOptions:?parmas, ??????}, ????}) ????router.goBack() ??} ??return?goBack }
使用:
詳情頁
import?{?useKeepAliveGoback?}?from?'@/hooks/useKeepAliveOptions' function?Index(){ ????... ????const?keepAliveGoback?=?useKeepAliveGoback()?//?用于給上一頁keepAlive的頁面?傳參 ????... ???? ????return?( ????????<> ????????????... ????????????<button?onClick={()?=>?{ ????????????????keepAliveGoback({?isAddSuccess:?true?})?//?給列表頁傳options ????????????}></button> ????????????... ????????</> ????) }
列表頁
import?{?useKeepAlivePageShow?}?from?'@/hooks/useKeepAliveOptions' function?Index(){ ????... ????//?options:?isAddSuccess?isEditSuccess ????useKeepAlivePageShow((options)?=>?{ ????????console.log('keepAlive?options',?options) ????????if?(options.isAddSuccess)?{ ??????????//?新建成功?//?列表頁碼變為1?并且刷新 ??????????search() ????????}?else?if?(options.isEditSuccess)?{ ??????????//?編輯成功?//?列表頁碼不變?并且刷新 ??????????getData() ????????} ????}) ???? ????... ???? ????return?<>...</> }
到此,關于“怎么使用react-activation實現keepAlive支持返回傳參”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注蝸牛博客網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:niceseo99@gmail.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。
評論