gaearon/react-hot-loader

RHL is recreating class arrow functions which breaks event listeners

Open

#1273 aperta il 27 giu 2019

Vedi su GitHub
 (10 commenti) (0 reazioni) (1 assegnatario)JavaScript (844 fork)batch import
discussionhelp wanted

Metriche repository

Star
 (12.265 star)
Metriche merge PR
 (Nessuna PR mergiata in 30 g)

Descrizione

Let's say I have a component like this:

class A extends Component {
    componentDidMount() {
        window.addEventListener('resize', this.onResize)
    }

    componentWillUnmount() {
        window.removeEventListener('resize', this.onResize)
    }

    onResize = () => {
        this.setState({...})
    }

    ...
}

This is my babel 7 config:

{
    presets: [
      [
        "@babel/preset-env",
        {
          loose: true,
          modules: "commonjs",
          targets: {
            chrome: "66",
          }
        }
      ],
      "@babel/preset-react"
    ],
    plugins: [
      'react-hot-loader/babel',
      'add-module-exports',
      ["@babel/plugin-proposal-decorators", {legacy: true}],
      ["@babel/plugin-proposal-class-properties", {loose: true}],
    ]
  }

Babel with plugin-proposal-class-properties transpiles above component to roughly something like this:

class A extends Component {
    constructor() {
        super()
        this.onResize = () => {
            this.setState({...})
        }
    }

    componentDidMount() {
        window.addEventListener('resize', this.onResize)
    }

    componentWillUnmount() {
        window.removeEventListener('resize', this.onResize)
    }
}

When hot reloading happens, this.onResize is redefined and points to a new function, not the function that was previously registered as a resize listener in didMount. When component gets unmounted, removeEventListener obviously doesn't work because this new onResize listener has never been registered, and as a result I have memory leak, React complaining about calling setState on an unmounted component and all sorts of bugs.

For now, I just hacked the RHL's code to prevent updating arrow functions at all (src/proxy/inject.js):

        if (!isArrow && (
          nextString !== String(prevAttr) ||
          (injectedBefore && nextString !== String(injectedBefore))
        ))

Perhaps you will come up with a more proper solution.

Guida contributor