溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

React Hooks在React-refresh模塊熱替換下的異常行為怎么解決

發(fā)布時間:2021-06-26 13:52:38 來源:億速云 閱讀:224 作者:chen 欄目:web開發(fā)

本篇內(nèi)容主要講解“React Hooks在React-refresh模塊熱替換下的異常行為怎么解決”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“React Hooks在React-refresh模塊熱替換下的異常行為怎么解決”吧!

 什么是 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.

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

React Hooks在React-refresh模塊熱替換下的異常行為怎么解決

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

react-refresh 的簡單原理

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

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

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

  • 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.

React Hooks在React-refresh模塊熱替換下的異常行為怎么解決

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

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

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

第一個問題

import React, { useEffect, useState } from 'react';  export default () => {   const [count, setState] = useState(0);    useEffect(() => {     setState(s => s + 1);   }, []);    return (     <div>       {count}     </div>   ) }

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

React Hooks在React-refresh模塊熱替換下的異常行為怎么解決

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

第二個問題

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

import React, { useEffect } from 'react'; import useUpdateEffect from './useUpdateEffect';  export default () => {    useEffect(() => {     console.log('執(zhí)行了 useEffect');   }, []);    useUpdateEffect(() => {     console.log('執(zhí)行了 useUpdateEffect');   }, []);    return (     <div>       hello world     </div>   ) }

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

React Hooks在React-refresh模塊熱替換下的異常行為怎么解決

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

import { useEffect, useRef } from 'react';  const useUpdateEffect: typeof useEffect = (effect, deps) => {   const isMounted = useRef(false);    useEffect(() => {     if (!isMounted.current) {       isMounted.current = true;     } else {       return effect();     }   }, deps); };  export default useUpdateEffect;

上面代碼的關鍵在 isMounted

初始化時,useEffect 執(zhí)行,標記 isMounted 為 true

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

第三個問題

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

import React, { useEffect, useState } from 'react';  function getUsername() {   console.log('請求了')   return new Promise(resolve => {     setTimeout(() => {       resolve('test');     }, 1000);   }); }  export default function IndexPage() {    const isUnmount = React.useRef(false);   const [loading, setLoading] = useState(true);    useEffect(() => {     setLoading(true);     getUsername().then(() => {       if (isUnmount.current === false) {         setLoading(false);       }     });     return () => {       isUnmount.current = true;     }   }, []);    return loading ? <div>loading</div> : <div>hello world</div>; }

如上代碼所示,在熱更新時,isUnmount 變?yōu)榱藅rue,導致二次執(zhí)行時,代碼以為組件已經(jīng)卸載了,不再響應異步操作。

如何解決這些問題

方案一

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

import { useEffect, useRef } from 'react';  const useUpdateEffect: typeof useEffect = (effect, deps) => {   const isMounted = useRef(false);  +  useEffect(() => { +   isMounted.current = false; +  }, []);      useEffect(() => {     if (!isMounted.current) {       isMounted.current = true;     } else {       return effect();     }   }, deps); };  export default useUpdateEffect;

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

方案二

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

/* @refresh reset */

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

到此,相信大家對“React Hooks在React-refresh模塊熱替換下的異常行為怎么解決”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關內(nèi)容可以進入相關頻道進行查詢,關注我們,繼續(xù)學習!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI