自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何實(shí)現(xiàn)React中的狀態(tài)自動(dòng)保存?

開(kāi)發(fā) 前端
作為程序員,當(dāng)然是盡可能懶啦,為了不需要每次都關(guān)心如何對(duì)數(shù)據(jù)進(jìn)行保存恢復(fù),我們需要研究如何自動(dòng)保存狀態(tài)

 [[278363]]

什么是狀態(tài)保存?

假設(shè)有下述場(chǎng)景:

移動(dòng)端中,用戶(hù)訪(fǎng)問(wèn)了一個(gè)列表頁(yè),上拉瀏覽列表頁(yè)的過(guò)程中,隨著滾動(dòng)高度逐漸增加,數(shù)據(jù)也將采用觸底分頁(yè)加載的形式逐步增加,列表頁(yè)瀏覽到某個(gè)位置,用戶(hù)看到了感興趣的項(xiàng)目,點(diǎn)擊查看其詳情,進(jìn)入詳情頁(yè),從詳情頁(yè)退回列表頁(yè)時(shí),需要停留在離開(kāi)列表頁(yè)時(shí)的瀏覽位置上

類(lèi)似的數(shù)據(jù)或場(chǎng)景還有已填寫(xiě)但未提交的表單、管理系統(tǒng)中可切換和可關(guān)閉的功能標(biāo)簽等,這類(lèi)數(shù)據(jù)隨著用戶(hù)交互逐漸變化或增長(zhǎng),這里理解為狀態(tài),在交互過(guò)程中,因?yàn)槟承┰蛐枰R時(shí)離開(kāi)交互場(chǎng)景,則需要對(duì)狀態(tài)進(jìn)行保存

在 React 中,我們通常會(huì)使用路由去管理不同的頁(yè)面,而在切換頁(yè)面時(shí),路由將會(huì)卸載掉未匹配的頁(yè)面組件,所以上述列表頁(yè)例子中,當(dāng)用戶(hù)從詳情頁(yè)退回列表頁(yè)時(shí),會(huì)回到列表頁(yè)頂部,因?yàn)榱斜眄?yè)組件被路由卸載后重建了,狀態(tài)被丟失

如何實(shí)現(xiàn) React 中的狀態(tài)保存

在 Vue 中,我們可以非常便捷地通過(guò) <keep-alive>[1] 標(biāo)簽實(shí)現(xiàn)狀態(tài)的保存,該標(biāo)簽會(huì)緩存不活動(dòng)的組件實(shí)例,而不是銷(xiāo)毀它們

而在 React 中并沒(méi)有這個(gè)功能,曾經(jīng)有人在官方提過(guò)功能 issues[2] ,但官方認(rèn)為這個(gè)功能容易造成內(nèi)存泄露,表示暫時(shí)不考慮支持,所以我們需要自己想辦法了

常見(jiàn)的解決方式:手動(dòng)保存狀態(tài)

手動(dòng)保存狀態(tài),是比較常見(jiàn)的解決方式,可以配合 React 組件的 componentWillUnmount 生命周期通過(guò) redux 之類(lèi)的狀態(tài)管理層對(duì)數(shù)據(jù)進(jìn)行保存,通過(guò) componentDidMount 周期進(jìn)行數(shù)據(jù)恢復(fù)

在需要保存的狀態(tài)較少時(shí),這種方式可以比較快地實(shí)現(xiàn)我們所需功能,但在數(shù)據(jù)量大或者情況多變時(shí),手動(dòng)保存狀態(tài)就會(huì)變成一件麻煩事了

作為程序員,當(dāng)然是盡可能懶啦,為了不需要每次都關(guān)心如何對(duì)數(shù)據(jù)進(jìn)行保存恢復(fù),我們需要研究如何自動(dòng)保存狀態(tài)

通過(guò)路由實(shí)現(xiàn)自動(dòng)狀態(tài)保存(通常使用 react-router)

既然 React 中狀態(tài)的丟失是由于路由切換時(shí)卸載了組件引起的,那可以嘗試從路由機(jī)制上去入手,改變路由對(duì)組件的渲染行為

我們有以下的方式去實(shí)現(xiàn)這個(gè)功能

    1.  重寫(xiě) <Route> 組件,可參考 react-live-route[4]

    重寫(xiě)可以實(shí)現(xiàn)我們想要的功能,但成本也比較高,需要注意對(duì)原始 <Route> 功能的保存,以及多個(gè) react-router 版本的兼容

    2.  替換路由庫(kù)為 react-keeper[5]

    完全替換掉路由方案是一個(gè)風(fēng)險(xiǎn)較大的事情,需要較為慎重地考慮3.

    3.  基于 <Route> 組件現(xiàn)有行為做拓展,可參考 react-router-cache-route[6]

    在閱讀了 <Route> 的源碼后發(fā)現(xiàn),如果使用 component 或者 render 屬性,都無(wú)法避免路由在不匹配時(shí)被卸載掉的命運(yùn)

    但將 children 屬性當(dāng)作方法來(lái)使用,我們就有手動(dòng)控制渲染的行為的可能,關(guān)鍵代碼在此處 https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Route.js#L41-L72

 

  1. // 節(jié)選自 Route 組件中的 render 函數(shù)  
  2. if (typeof children === "function") {  
  3.      childrenchildren = children(props); // children 是函數(shù)時(shí),將對(duì) children 進(jìn)行調(diào)用得到真實(shí)的渲染結(jié)果  
  4. if (children === undefined) {  
  5.        ...  
  6.        children = null 
  7.      }  
  8.    }  
  9. return (  
  10. <RouterContext.Provider value={props}>  
  11.        {children && !isEmptyChildren(children) // children 存在時(shí),將使用 children 進(jìn)行渲染  
  12.          ? children  
  13.          : props.match  
  14.            ? component  
  15.              ? React.createElement(component, props)  
  16.              : render  
  17.                ? render(props)  
  18.                : null // 使用 render 屬性無(wú)法阻止組件的卸載  
  19.            : null // 使用 component 屬性無(wú)法阻止組件的卸載  
  20.        }  
  21. </RouterContext.Provider>  
  22.    ); 

基于上述源碼探究,我們可以對(duì) <Route> 進(jìn)行拓展,將 <Route> 的不匹配行為由卸載調(diào)整為隱藏,如下 

  1. <Route exact path="/list">  
  2.      {props => (  
  3.          <div style={props.match ? null : { display: 'none' }}>  
  4.              <List {...props} />  
  5.          </div>  
  6.      )}  
  7.  </Route> 

上述是最簡(jiǎn)的調(diào)整方式,實(shí)際情況中也需要考慮隱藏狀態(tài)下 match 為 null 導(dǎo)致組件報(bào)錯(cuò)的問(wèn)題,且由于不再是組件卸載,所以和 TransitionGroup 配合得不好,導(dǎo)致轉(zhuǎn)場(chǎng)動(dòng)畫(huà)難以實(shí)現(xiàn)

使用 react-router-cache-route[7],得到的效果大致如下圖,

上述探究了通過(guò)路由入手實(shí)現(xiàn)自動(dòng)狀態(tài)保存的可能,以及現(xiàn)有的實(shí)現(xiàn),但終究不是真實(shí)的、純粹的 KeepAlive 功能,接下來(lái)我們嘗試探究真實(shí) KeepAlive 功能的實(shí)現(xiàn)

模擬真實(shí)的 <KeepAlive> 功能

以下是期望的使用方式

 

  1. function App() {  
  2. const [show, setShow] = useState(true)  
  3. return (  
  4. <div>  
  5. <button onClick={() => setShow(show => !show)}>Toggle</button>  
  6.       {show && (  
  7. <KeepAlive>  
  8. <Test />  
  9. </KeepAlive>  
  10.       )}  
  11. </div>  
  12.   )  

實(shí)現(xiàn)原理說(shuō)起來(lái)較為簡(jiǎn)單,由于 React 會(huì)卸載掉處于固有組件層級(jí)內(nèi)的組件,所以我們需要將 <KeepAlive> 中的組件,也就是其 children 屬性抽取出來(lái),渲染到一個(gè)不會(huì)被卸載的組件內(nèi),就可以實(shí)現(xiàn)此功能

以下是 react-activation[8] 的實(shí)現(xiàn)效果

在線(xiàn)示例[9]

實(shí)際實(shí)現(xiàn)過(guò)程中,遇到了許多問(wèn)題,都是由于打破了原有 React 層級(jí)關(guān)系引起的,例如

  •  渲染延遲
  •  Provider 上下文功能失效
  •  Error Boundaries 失效
  •  React.Suspense & React.lazy 失效
  •  React 合成事件冒泡失效
  •  其他未發(fā)現(xiàn)的功能

但上述問(wèn)題,大多數(shù)是可以通過(guò)橋接機(jī)制修復(fù)的

相同的、更早的實(shí)現(xiàn)還有 react-keep-alive[10] 

結(jié)語(yǔ)

狀態(tài)緩存是應(yīng)用中十分常見(jiàn)的需求,在需要處理的數(shù)據(jù)量較少時(shí),使用手動(dòng)狀態(tài)緩存就可以解決大多數(shù)問(wèn)題,但當(dāng)情況復(fù)雜時(shí),還需要嘗試將緩存功能單獨(dú)拎出來(lái)解決,以便在業(yè)務(wù)開(kāi)發(fā)過(guò)程中更好地進(jìn)行關(guān)注點(diǎn)分離

目前的實(shí)現(xiàn)都有各自的問(wèn)題,但其探究過(guò)程十分有趣,最好的方式仍是官方的支持,但目前還不能報(bào)太大期望

 

 

責(zé)任編輯:龐桂玉 來(lái)源: 前端大全
相關(guān)推薦

2009-09-01 18:06:06

c#保存窗體狀態(tài)

2020-10-21 08:38:47

React源碼

2022-03-29 20:10:27

React狀態(tài)管理

2023-01-01 23:42:22

React框架暗黑模式

2022-03-18 14:09:52

ReactJavaScript

2021-11-16 19:37:03

緩存

2018-04-18 08:54:28

RDD內(nèi)存Spark

2022-10-26 15:22:31

React組件User組件

2022-05-15 22:08:58

ReactHookdebounce

2023-01-29 08:00:00

Instagram濾鏡圖片編輯

2011-05-16 11:29:00

MySQL自動(dòng)備份

2021-06-03 09:31:56

React狀態(tài)模式

2022-04-14 09:01:39

React源碼Flow

2024-10-11 15:04:35

KafkaLeader選舉

2021-05-23 15:46:23

React代碼前端

2024-07-02 10:00:55

2023-10-31 08:32:59

2020-11-30 06:18:21

React

2010-03-30 14:08:53

Nginx狀態(tài)監(jiān)控

2025-01-14 00:00:00

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)