成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

React Hooks 在 React-refresh 模塊熱替換(HMR)下的異常行為

開發 前端
本篇文章主要講解 React Hooks 在 react-refresh 模式下的怪異行為,現在我來看下 react-refresh 對函數組件的工作機制。

[[400802]]

 什么是 react-refresh

react-refresh-webpack-plugin[1] 是 React 官方提供的一個 模塊熱替換(HMR)插件。

  • A Webpack plugin to enable "Fast Refresh" (also previously known as Hot Reloading) for React components.

在開發環境編輯代碼時,react-refresh 可以保持組件當前狀態,僅僅變更編輯的部分。在 umi[2] 中可以通過 fastRefresh: {}快速開啟該功能。

圖片

 

這張 gif 動圖展示的是使用 react-refresh 特性的開發體驗,可以看出,修改組件代碼后,已經填寫的用戶名和密碼保持不變,僅僅只有編輯的部分變更了。

react-refresh 的簡單原理

對于 Class 類組件,react-refresh 會一律重新刷新(remount),已有的 state 會被重置。而對于函數組件,react-refresh 則會保留已有的 state。所以 react-refresh 對函數類組件體驗會更好。本篇文章主要講解 React Hooks 在 react-refresh 模式下的怪異行為,現在我來看下 react-refresh 對函數組件的工作機制。

在熱更新時為了保持狀態,useState 和 useRef 的值不會更新。

在熱更新時,為了解決某些問題[3],useEffect、useCallback、useMemo 等會重新執行。

  • When we update the code, we need to "clean up" the effects that hold onto past values (e.g. passed functions), and "setup" the new ones with updated values. Otherwise, the values used by your effect would be stale and "disagree" with value used in your rendering, which makes Fast Refresh much less useful and hurts the ability to have it work with chains of custom Hooks.
圖片

如上圖所示,在文本修改之后,state保持不變,useEffect被重新執行了。

react-refresh 工作機制導致的問題

在上述工作機制下,會帶來很多問題,接下來我會舉幾個具體的例子。

第一個問題

  1. import React, { useEffect, useState } from 'react'
  2.  
  3. export default () => { 
  4.   const [count, setState] = useState(0); 
  5.  
  6.   useEffect(() => { 
  7.     setState(s => s + 1); 
  8.   }, []); 
  9.  
  10.   return ( 
  11.     <div> 
  12.       {count
  13.     </div> 
  14.   ) 

 上面的代碼很簡單,在正常模式下,count值最大為 1。因為 useEffect 只會在初始化的時候執行一次。但在 react-refresh 模式下,每次熱更新的時候,state 不變,但 useEffect 重新執行,就會導致 count 的值一直在遞增。

圖片

如上圖所示,count 隨著每一次熱更新在遞增。

第二個問題

如果你使用了 ahooks[4] 或者 react-use[5] 的 useUpdateEffect,在熱更新模式下也會有不符合預期的行為。

  1. import React, { useEffect } from 'react'
  2. import useUpdateEffect from './useUpdateEffect'
  3.  
  4. export default () => { 
  5.  
  6.   useEffect(() => { 
  7.     console.log('執行了 useEffect'); 
  8.   }, []); 
  9.  
  10.   useUpdateEffect(() => { 
  11.     console.log('執行了 useUpdateEffect'); 
  12.   }, []); 
  13.  
  14.   return ( 
  15.     <div> 
  16.       hello world 
  17.     </div> 
  18.   ) 

 useUpdateEffect 與 useEffect相比,它會忽略第一次執行,只有在 deps 變化時才會執行。以上代碼的在正常模式下,useUpdateEffect 是永遠不會執行的,因為 deps 是空數組,永遠不會變化。但在 react-refresh 模式下,熱更新時,useUpdateEffect 和 useEffect 同時執行了。

圖片

造成這個問題的原因,就是 useUpdateEffect 用 ref 來記錄了當前是不是第一次執行,見下面的代碼。

  1. import { useEffect, useRef } from 'react'
  2.  
  3. const useUpdateEffect: typeof useEffect = (effect, deps) => { 
  4.   const isMounted = useRef(false); 
  5.  
  6.   useEffect(() => { 
  7.     if (!isMounted.current) { 
  8.       isMounted.current = true
  9.     } else { 
  10.       return effect(); 
  11.     } 
  12.   }, deps); 
  13. }; 
  14.  
  15. export default useUpdateEffect; 

上面代碼的關鍵在 isMounted

初始化時,useEffect 執行,標記 isMounted 為 true

熱更新后,useEffect 重新執行了,此時 isMounted 為 true,就往下執行了

第三個問題

最初發現這個問題,是 ahooks 的 useRequest 在熱更新后,loading 會一直為 true。經過分析,原因就是使用 isUnmount ref 來標記組件是否卸載。

  1. import React, { useEffect, useState } from 'react'
  2.  
  3. function getUsername() { 
  4.   console.log('請求了'
  5.   return new Promise(resolve => { 
  6.     setTimeout(() => { 
  7.       resolve('test'); 
  8.     }, 1000); 
  9.   }); 
  10.  
  11. export default function IndexPage() { 
  12.  
  13.   const isUnmount = React.useRef(false); 
  14.   const [loading, setLoading] = useState(true); 
  15.  
  16.   useEffect(() => { 
  17.     setLoading(true); 
  18.     getUsername().then(() => { 
  19.       if (isUnmount.current === false) { 
  20.         setLoading(false); 
  21.       } 
  22.     }); 
  23.     return () => { 
  24.       isUnmount.current = true
  25.     } 
  26.   }, []); 
  27.  
  28.   return loading ? <div>loading</div> : <div>hello world</div>; 

 如上代碼所示,在熱更新時,isUnmount 變為了true,導致二次執行時,代碼以為組件已經卸載了,不再響應異步操作。

如何解決這些問題

方案一

第一個解決方案是從代碼層面解決,也就是要求我們在寫代碼的時候,時時能想起來 react-refresh 模式下的怪異行為。比如 useUpdateEffect 我們就可以在初始化或者熱替換時,將 isMounted ref 初始化掉。如下:

  1. import { useEffect, useRef } from 'react'
  2.  
  3. const useUpdateEffect: typeof useEffect = (effect, deps) => { 
  4.   const isMounted = useRef(false); 
  5.  
  6. +  useEffect(() => { 
  7. +   isMounted.current = false
  8. +  }, []); 
  9.    
  10.   useEffect(() => { 
  11.     if (!isMounted.current) { 
  12.       isMounted.current = true
  13.     } else { 
  14.       return effect(); 
  15.     } 
  16.   }, deps); 
  17. }; 
  18.  
  19. export default useUpdateEffect; 

這個方案對上面的問題二和三都是有效的。

方案二

根據官方文檔[6],我們可以通過在文件中添加以下注釋來解決這個問題。

  1. /* @refresh reset */ 

添加這個問題后,每次熱更新,都會 remount,也就是組件重新執行。useState 和 useRef 也會重置掉,也就不會出現上面的問題了。

官方態度

本來 React Hooks 已經有蠻多潛規則了,在使用 react-refresh 時,還有潛規則要注意。但官方回復說這是預期行為,見該 issue[7]

  • Effects are not exactly "mount"/"unmount" — they're more like "show"/"hide".

不管你暈沒暈,反正我是暈了,╮(╯▽╰)╭。

參考資料

[1]react-refresh-webpack-plugin:

https://github.com/pmmmwh/react-refresh-webpack-plugin

[2]umi:

https://umijs.org/zh-CN/docs/fast-refresh

[3]為了解決某些問題:

https://github.com/facebook/react/issues/21019#issuecomment-800650091

[4]ahooks:

https://github.com/alibaba/hooks/blob/master/packages/hooks/src/useUpdateEffect/index.ts

[5]react-use:

https://github.com/streamich/react-use/blob/master/docs/useUpdateEffect.md

[6]官方文檔:

https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/API.md#reset

[7]issue:

https://github.com/facebook/react/issues/21019

 

責任編輯:姜華 來源: 前端技術磚家
相關推薦

2019-08-20 15:16:26

Reacthooks前端

2022-08-21 09:41:42

ReactVue3前端

2023-11-06 08:00:00

ReactJavaScript開發

2021-03-18 08:00:55

組件Hooks React

2020-10-28 09:12:48

React架構Hooks

2022-03-31 17:54:29

ReactHooks前端

2020-09-19 17:46:20

React Hooks開發函數

2021-05-18 08:21:38

React HooksReact前端

2019-03-13 10:10:26

React組件前端

2021-02-02 11:02:20

React任務饑餓行為優先級任務

2022-04-16 20:10:00

React Hookfiber框架

2021-09-26 09:40:25

React代碼前端

2022-07-18 09:01:58

React函數組件Hooks

2021-05-11 08:48:23

React Hooks前端

2023-05-08 07:52:29

JSXReactHooks

2022-02-10 19:15:18

React監聽系統模式

2020-08-10 06:31:01

React Hooks前端開發

2021-11-05 10:36:19

性能優化實踐

2022-03-16 22:24:50

ReactstateHooks

2023-02-02 08:41:14

React團隊Vite
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: jizz亚洲人| 亚洲在线日韩 | 成年免费在线观看 | 久久久久久久一区二区三区 | 欧美激情一区 | 国产精品久久久久久久免费大片 | 毛片一级片 | 91天堂网| 精品一区视频 | 亚洲一二三视频 | 亚洲视频三 | 亚洲视频三| 成人精品久久 | 日日夜夜天天 | 视频在线一区 | 精品一区二区在线观看 | 久久久久久久久久久久一区二区 | 国产精品自产拍在线观看蜜 | 超碰在线播 | 自拍偷拍欧美 | 久久精品免费观看 | 在线中文字幕亚洲 | 日本成人片在线观看 | 午夜精品一区二区三区在线播放 | 99久久精品免费看国产免费软件 | 国产视频第一页 | 亚洲看片网站 | 成人在线视频免费观看 | 欧美成人一区二免费视频软件 | 一区二区中文字幕 | 免费在线观看av网站 | 一级一片在线观看 | 视频在线一区 | 国产一区免费 | 在线一区二区观看 | 国内精品成人 | 欧美一级三级 | 亚洲精选久久 | 91高清视频在线 | 久久久看| 噜啊噜在线 |