react-refresh-webpack-plugin[1]是 React 官方提供的一个 模块热替换(HMR)插件。

10年的繁昌网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。营销型网站建设的优势是能够根据用户设备显示端的尺寸不同,自动调整繁昌建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。创新互联从事“繁昌网站设计”,“繁昌网站推广”以来,每个客户项目都认真落实执行。
在开发环境编辑代码时,react-refresh 可以保持组件当前状态,仅仅变更编辑的部分。在umi[2]中可以通过 fastRefresh: {}快速开启该功能。
这张 gif 动图展示的是使用 react-refresh 特性的开发体验,可以看出,修改组件代码后,已经填写的用户名和密码保持不变,仅仅只有编辑的部分变更了。
对于 Class 类组件,react-refresh 会一律重新刷新(remount),已有的 state 会被重置。而对于函数组件,react-refresh 则会保留已有的 state。所以 react-refresh 对函数类组件体验会更好。本篇文章主要讲解 React Hooks 在 react-refresh 模式下的怪异行为,现在我来看下 react-refresh 对函数组件的工作机制。
在热更新时为了保持状态,useState 和 useRef 的值不会更新。
在热更新时,为了解决某些问题[3],useEffect、useCallback、useMemo 等会重新执行。
如上图所示,在文本修改之后,state保持不变,useEffect被重新执行了。
在上述工作机制下,会带来很多问题,接下来我会举几个具体的例子。
- import React, { useEffect, useState } from 'react';
 - export default () => {
 - const [count, setState] = useState(0);
 - useEffect(() => {
 - setState(s => s + 1);
 - }, []);
 - return (
 - {count}
 - )
 - }
 
上面的代码很简单,在正常模式下,count值最大为 1。因为 useEffect 只会在初始化的时候执行一次。但在 react-refresh 模式下,每次热更新的时候,state 不变,但 useEffect 重新执行,就会导致 count 的值一直在递增。
如上图所示,count 随着每一次热更新在递增。
如果你使用了ahooks[4]或者react-use[5]的 useUpdateEffect,在热更新模式下也会有不符合预期的行为。
- import React, { useEffect } from 'react';
 - import useUpdateEffect from './useUpdateEffect';
 - export default () => {
 - useEffect(() => {
 - console.log('执行了 useEffect');
 - }, []);
 - useUpdateEffect(() => {
 - console.log('执行了 useUpdateEffect');
 - }, []);
 - return (
 - hello world
 - )
 - }
 
useUpdateEffect 与 useEffect相比,它会忽略第一次执行,只有在 deps 变化时才会执行。以上代码的在正常模式下,useUpdateEffect 是永远不会执行的,因为 deps 是空数组,永远不会变化。但在 react-refresh 模式下,热更新时,useUpdateEffect 和 useEffect 同时执行了。
造成这个问题的原因,就是 useUpdateEffect 用 ref 来记录了当前是不是第一次执行,见下面的代码。
- 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 执行,标记 isMounted 为 true
热更新后,useEffect 重新执行了,此时 isMounted 为 true,就往下执行了
最初发现这个问题,是 ahooks 的 useRequest 在热更新后,loading 会一直为 true。经过分析,原因就是使用 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 ?
 loading:hello world;- }
 
如上代码所示,在热更新时,isUnmount 变为了true,导致二次执行时,代码以为组件已经卸载了,不再响应异步操作。
第一个解决方案是从代码层面解决,也就是要求我们在写代码的时候,时时能想起来 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;
 
这个方案对上面的问题二和三都是有效的。
根据官方文档[6],我们可以通过在文件中添加以下注释来解决这个问题。
- /* @refresh reset */
 
添加这个问题后,每次热更新,都会 remount,也就是组件重新执行。useState 和 useRef 也会重置掉,也就不会出现上面的问题了。
本来 React Hooks 已经有蛮多潜规则了,在使用 react-refresh 时,还有潜规则要注意。但官方回复说这是预期行为,见该issue[7]。
不管你晕没晕,反正我是晕了,╮(╯▽╰)╭。
[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
                当前文章:ReactHooks在React-refresh模块热替换(HMR)下的异常行为
                
                网站链接:http://www.csdahua.cn/qtweb/news29/52929.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网