"react-hot-loader/babel" 破坏了我的构建——为什么需要它?

"react-hot-loader/babel" breaks my build -- why is it needed?

一旦我将 "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 -- 
        return <canvas className={this.props.className} ref={this.setRef} tabIndex={0} />;
    }
}

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

setRef
this.canvas undefined

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

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

所以,我的问题是:

  1. "react-hot-loader/babel"实际上是做什么的?
  2. 如何才能破坏我的class属性?

这是 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
                    }
                ]
                // 
                //"react-hot-loader/babel"
            ],
            "presets": [
                [
                    "env",
                    {
                        "targets": {
                            "browsers": "last 2 chrome versions"
                        },
                        "modules": false
                    }
                ]
            ],
        },
        "webpack": {
            "presets": [
                [
                    "env",
                    {
                        "targets": {
                            "node": "current"
                        },
                        "modules": "commonjs"
                    }
                ]
            ]
        }
    }
}

这似乎是 react-hot-loader v4.

中的 bug in react-hot-loader v3 (I was able to reproduce the issue) and it is 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 验证了有问题的代码,它已修复。

PS: 使用答案作为注释,因为我需要格式化

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

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);
}

如果你使用上面的方法就可以了,因为 get same this then。或者您需要将 setRef 更改为 lambda

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