react-hot-loader/babel ломает мою сборку, зачем это нужно?

Как только я добавляю "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:

setRef
this.canvas не определено

Что довольно странно, потому что мы видим, что он устанавливает this.canvas перед вызовом componentDidMount, так что я не знаю, что react-hot-loader/babel делает, чтобы сломать это.

Без react-hot-loader/babel все нормально работает, включая горячую перезагрузку.

Итак, мои вопросы:

  1. Что на самом деле делает «реагировать-горячий-загрузчик/бабель»?
  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"
                    }
                ]
            ]
        }
    }
}

person mpen    schedule 18.02.2018    source источник
comment
Какой-то это обязательный вопрос?   -  person CodesInChaos    schedule 19.02.2018
comment
Я проверил ваш код в новой установке приложения create-react-app, и он работает с предложениями ES7, поэтому я предполагаю, что в вашей конфигурации веб-пакета отсутствует какая-либо директива при добавлении react-hot-loader.   -  person Dez    schedule 19.02.2018
comment
@Dez Спасибо, Дез. Какую директиву я мог пропустить? Вот мой файл веб-пакета: gist.github.com/mnpenner/df723cb33682c137290caefaf890444e   -  person mpen    schedule 19.02.2018
comment
@mpen, можешь создать минимальный проект с проблемой? Поможет решить вашу проблему намного быстрее   -  person Tarun Lalwani    schedule 24.02.2018
comment
@TarunLalwani Здесь: transfer.sh/9i0du/stack48857689.zip Запустите yarn, а затем yarn dev, чтобы начать это (или npm i и npm run dev, если хотите)   -  person mpen    schedule 24.02.2018
comment
@mpen, это работает для меня как таковое. Какую проблему вы получаете?   -  person Tarun Lalwani    schedule 24.02.2018
comment
@mpen, какую версию react-hot-loader вы используете? (Можете ли вы поделиться своей версией package.json react-hot-loader, т.е.: "react-hot-loader": "3.0.0-beta.6")   -  person Aaqib    schedule 24.02.2018
comment
@Aaqib Это в файле yarn.lock, который я опубликовал: 3.1.3   -  person mpen    schedule 25.02.2018
comment
@TarunLalwani Ты смотрел на консоль? i.imgur.com/heSN6DR.png компонентDidMount получает undefined. Он не должен быть неопределенным.   -  person mpen    schedule 25.02.2018


Ответы (2)


Похоже, это ошибка в react-hot-loader v3 ( Мне удалось воспроизвести проблему), и она исправлена в реактивном горячем загрузчике v4.

Согласно этому комментарию, эта проблема кажется вызвано логикой проксирования в 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>
    }
}

Или, как указано в следующем комментарии, ваши привязки функций выполняются в 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. и там это зафиксировано.

person Bless    schedule 24.02.2018
comment
Ага. Это похоже на ошибку и обходной путь. Вы получаете очки (спасибо). Я действительно хотел бы, чтобы кто-то ответил на другую половину моего вопроса. Что, черт возьми, должен делать react-hot-loader/babel, кроме разрушения крушения? Потому что HMR работает с ним или без него. - person mpen; 25.02.2018
comment
react-hot-loader предназначен для сохранения внутреннего состояния компонента. HMR будет работать и без него; но состояние компонента будет потеряно после каждой перезагрузки. Возможно, вы захотите прочитать этот пост Дэна Абрамова, чтобы узнать, что происходит под капотом — medium.com/@dan_abramov/hot-reloading-in-react-1140438583bf. - person Bless; 25.02.2018

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

Если вы используете вышеизложенное, это сработает, потому что вы получите то же самое. Или вам нужно изменить setRef на лямбда

return <div ref={ node => this.div=node }>test</div>
person Tarun Lalwani    schedule 24.02.2018
comment
Ага! Спасибо, что объяснили, что здесь происходит. - person mpen; 25.02.2018