【问题标题】:"react-hot-loader/babel" breaks my build -- why is it needed?“react-hot-loader/babel”破坏了我的构建——为什么需要它?
【发布时间】:2018-07-29 04:30:22
【问题描述】:

只要我将"react-hot-loader/babel" 添加到我的.babelrc,它就会破坏我的 React 组件。

具体来说,我有一些看起来像这样的代码:

export default class Editor extends React.Component {

    componentDidMount() {
        console.log('this.canvas',this.canvas);
        // ...
    }

    setRef = node => {
        console.log('setRef');
        this.canvas = node;
    }

    render() {
        // tabIndex is needed to receive keyboard events -- https://stackoverflow.com/a/12887221/65387
        return <canvas className={this.props.className} ref={this.setRef} tabIndex={0} />;
    }
}

当我运行它时,我在我的 Chrome 开发工具中看到了这个:

设置参考
this.canvas 未定义

这很奇怪,因为我们可以看到它在设置 this.canvas 之前 调用 componentDidMount 所以我不知道 react-hot-loader/babel 正在做什么来破坏它。

没有react-hot-loader/babel,一切正常,包括热重载。

所以,我的问题是:

  1. “react-hot-loader/babel”实际上是做什么的?
  2. 如何让它破坏我的类属性?

这适用于 React 16.1、react-hot-loader 3、webpack 3.11、babel 6.x


我的.babelrc,如果你想看到的话:

{
    "plugins": [
        "transform-object-rest-spread",
        "syntax-jsx",
        "transform-react-jsx",
        "transform-react-display-name",
        "transform-class-properties",
        "transform-function-bind",
        "transform-decorators-legacy"
    ],
    "compact": false,
    "env": {
        "development": {
            "plugins": [
                "transform-react-jsx-self",
                "transform-react-jsx-source",
                [
                    "styled-components",
                    {
                        "displayName": true,
                        "minify": false
                    }
                ]
                // https://stackoverflow.com/q/48857689/65387
                //"react-hot-loader/babel"
            ],
            "presets": [
                [
                    "env",
                    {
                        "targets": {
                            "browsers": "last 2 chrome versions"
                        },
                        "modules": false
                    }
                ]
            ],
        },
        "webpack": {
            "presets": [
                [
                    "env",
                    {
                        "targets": {
                            "node": "current"
                        },
                        "modules": "commonjs"
                    }
                ]
            ]
        }
    }
}

【问题讨论】:

  • 这个绑定问题?
  • 我在全新安装的 create-react-app 中检查了您的代码,它适用于 ES7 提案,所以我猜测您的 webpack 配置在添加 react-hot-loader 时缺少任何指令。
  • @Dez 谢谢 Dez。我可能缺少什么指令?这是我的 webpack 文件:gist.github.com/mnpenner/df723cb33682c137290caefaf890444e
  • @mpen,你能创建一个有问题的最小项目吗?将有助于更快地解决您的问题
  • @TarunLalwani 在这里:transfer.sh/9i0du/stack48857689.zip 运行yarn 然后yarn dev 启动它(或者npm inpm run dev,如果你愿意的话)

标签: reactjs webpack react-hot-loader


【解决方案1】:

这似乎是 react-hot-loader v3 中的 bug(我能够重现该问题),它是 react-hot-loader v4 中的 fixed

根据this comment,这个问题似乎是由 react-proxy 中的代理逻辑引起的。一个技巧是存储一个对象来保存你的引用,这将被 react-proxy 复制到代理,并且将在 this 的代理版本中可用:

export default class App extends React.Component {
    constructor(props) {
        super(props);
        this.myRefs = {};
        this.setRef = this.setRef.bind(this);
    }

    componentDidMount() {
        console.log('componentDidMount',this.myRefs.div); // <-- Not null!
    }

    setRef(node) {
        this.myRefs.div = node;
        console.log('setRef',this.myRefs.div);
    };

    render() {
        return <div ref={this.setRef}>test</div>
    }
}

或如next comment 中所述,确保您的函数绑定在componentWillMount 中完成:

export default class App extends React.Component {
    constructor(props) {
        super(props);
        // Do not bind here, "this" will get proxied.
    }

    // Proxy "this" is now available. Bind here.
    componentWillMount() {
        this.setRef = this.setRef.bind(this)
    }

    componentDidMount() {
        console.log('componentDidMount',this.div); // <-- Not null!
    }

    setRef(node) {
        this.div = node;
        console.log('setRef',this.div);
    };

    render() {
        return <div ref={this.setRef}>test</div>
    }
}

我确实使用 react-hot-loader v4 验证了有问题的代码,并且已修复。

【讨论】:

  • 是的。这看起来像错误,也是一种解决方法。你得到积分(谢谢)。我确实希望有人能回答我的另一半问题。除了破坏破坏之外,react-hot-loader/babel 到底应该做什么?因为无论有没有它,HMR 都可以工作。
  • react-hot-loader 用于保存内部组件状态。没有它,HMR 也可以工作;但是每次重新加载后组件状态都会丢失。您可能想阅读 Dan Abramov 的这篇文章,以了解幕后情况 - medium.com/@dan_abramov/hot-reloading-in-react-1140438583bf
【解决方案2】:

PS:在我需要格式化时使用答案作为 cmets

我能够重现该问题。出现此问题是因为热加载器将代码更改为

setRef = (...params) => this.__setRef__REACT_HOT_LOADER__(...params)

然后在组件的prototype

__setRef__REACT_HOT_LOADER__(node) {
        console.log("setRef called", this, node);
        if (this === null) return;
        if (node === null) return;
        console.log(node);
        this.div = node;
    }

我相信这会破坏this 链,否则这个副本会变得不同。有两个变化会起作用

componentDidMount = () => {
    console.log('componentDidMount',this.div);
}

如果你使用上面的,那么它会起作用,因为得到相同的这个。或者您需要将 setRef 更改为 lambda

return <div ref={ node => this.div=node }>test</div>

【讨论】:

  • 啊哈!感谢您解释这里发生了什么。
猜你喜欢
  • 2020-06-05
  • 1970-01-01
  • 1970-01-01
  • 2018-04-12
  • 2017-06-25
  • 2016-05-30
  • 2020-07-12
  • 2016-08-09
  • 2015-12-03
相关资源
最近更新 更多