React-hot-loader 更新时组件状态丢失

我在使用 React-hot-loader 的过程中遇到一个问题,每次更新 React 组件的内部状态都会丢失。

具体的情况是这样的,在将其他配置工作做好以后,我按照使用教程在 .babelrc 文件中添加了这样一行代码:

"plugins": ["react-hot-loader/babel"],

完成上面工作以后运行 webpack-dev-server --hot --progress --env dev 命令。

这时我发现,当我修改了页面代码以后,xxx.hot-update.js 文件被添加到了 head 标签中,但是页面并没有发生任何变化,改动的地方没有被刷新,当然,React 组件的内部状态也没有发生任何变化。

然后我将上面 .babelrc 文件中的那行代码去掉,也就是不使用 React-hot-loader 提供的 Babel 插件。

然后运行 webpack-dev-server --hot --progress --env dev 命令。

此时,当我修改了页面代码以后,xxx.hot-update.js 文件同样被添加到了 head 标签中,页面改动的地方确实发生了变化,但是与此同时 React 组件的内部状态也丢失了!

我反复查看以后发现,此时的代码更新似乎是将 reactroot 这个元素整个替换掉了,而不是仅仅将有变化的组件单独更新!

上面两种情况的区别就是是否添加 "plugins": ["react-hot-loader/babel"], 这一行代码。

按照官方文档来说,这一行代码是一定要添加到 .babelrc 文件中的,但是我添加以后就出现了上面所说的第一种情况,迫不得已,我目前只能删掉这句代码,暂且让 React-hot-loader 将我的 reactroot 元素每次整体替换掉。

有没有同学有过类似的情况,烦请赐教,不胜感谢!

这里是项目的 webpack 配置信息:webpack.config.js

阅读 5.2k
3 个回答

半个月之前提出的问题了,后来确实解决了,但不是按照上面回答中的方式解决的,因为我的项目配置本身没有问题。

出现这个问题的原因是我在开发过程中使用了react-loadable这个按需加载的工具,它与react-hot-loader不可以完全兼容,这个问题一直存在,在react-hot-loader的 issue 中已经有提到过这个现象了,但是作者已经将近半年没有更新过react-hot-loader,所以想要修复这个问题的话,估计真是够等的。

好在我最后还是在 Github 上找到了解决办法,是一个俄罗斯程序员提供的,他将react-hot-loader自己包装了一层,实现了在按需加载的情况下仍然能够使热替换生效,他的这个工具已经发布在了 Github 上,名字叫做react-hot-component-loader链接在这里,如果有同学出现了跟我同样的问题,可以尝试一下他的这个工具,我个人认为还是挺好用的,但是目前的版本只是到了 1.0.3,而且唯一更新的两次都是我给他测试了一下,发现了问题更新的……估计其中存在的问题应该还是有的,但是就我现在来说已经够用了。

再贴一个我跟他讨论的链接吧,大家可以从我跟他讨论的过程中发现自己的问题是否跟我一样。

大哥,你仔细看文档,你这步骤肯定少了,文档上一共有三部。就我下面的三步骤。

另外,也可以看我之前写的一个教程模块热替换(Hot Module Replacement)

  1. 命令行加 --hot

  2. .babelrc 增加 react-hot-loader/babel

  3. webpack.config.js入口增加react-hot-loader/patch

        entry: [
            'react-hot-loader/patch',
            path.join(__dirname, 'src/index.js')
        ]
  4. 入口文件要用AppContainer包裹起来。并且要使用if(module.hot){...}

    import React from 'react';
    import ReactDom from 'react-dom';
    import {AppContainer} from 'react-hot-loader';
    
    import getRouter from './router/router';
    
    /*初始化*/
    renderWithHotReload(getRouter());
    
    /*热更新*/
    if (module.hot) {
        module.hot.accept('./router/router', () => {
            const getRouter = require('./router/router').default;
            renderWithHotReload(getRouter());
        });
    }
    
    function renderWithHotReload(RootElement) {
        ReactDom.render(
            <AppContainer>
                {RootElement}
            </AppContainer>,
            document.getElementById('app')
        )
    }
推荐问题
宣传栏