react-hot-loader: Methods closure is lost after hot reload

There are several forms in my project. So I created a generic input change handler:

//Very simplistic example of universal-handler.js
export default context => e => {
   const { name, value } = e.target
   context.setState({
      [name]: value
   })
}

And I use it like this:

import { hot } from 'react-hot-loader'
import handle from './universal-handler'

class SomeForm extends React.Component {
   constructor() {
      super()
      this.state = { login: '' }
      this.handle = handle(this)
   }

   render() {
      return (
         <form>
            <input
               type='text'
               name='login'
               value={this.state.login}
               onChange={this.handle}
            />
         </form>
      )
   }
}

export default hot(module)(SomeForm)

First everything goes well. But after a hot reload, when I start typing to the input, I get an error:

Uncaught ReferenceError: context is not defined
at eval (eval at __reactstandin__regenerateByEval (VM48923
App.js:NaN), <anonymous>:10:3)
at HTMLUnknownElement.callCallback (react-dom.development.js:100)
at Object.invokeGuardedCallbackDev (react-dom.development.js:138)
at Object.invokeGuardedCallback (react-dom.development.js:187)
at Object.invokeGuardedCallbackAndCatchFirstError
(react-dom.development.js:201)
at executeDispatch (react-dom.development.js:466)
at executeDispatchesInOrder (react-dom.development.js:488)
at executeDispatchesAndRelease (react-dom.development.js:586)
at executeDispatchesAndReleaseTopLevel
(react-dom.development.js:597)
at Array.forEach (<anonymous​>)

Because the context is lost:

(function REACT_HOT_LOADER_SANDBOX () {
          var _this  = this; // common babel transpile
          var _this2 = this; // common babel transpile
          var _this3 = this; // common babel transpile
          return function (e) {
		var _e$target = e.target,
		    name = _e$target.name,
		    value = _e$target.value;

		context.setState(_defineProperty({}, name, value));
	};
          }).call(this)

About this issue

Commits related to this issue

Most upvoted comments

Going first to “fix” RHL, “back to 4.1.2” behavior (just dont fall), then - implement a new way to update component properties.

Ok, let’s try to find a way, to detect, and repeat functions like this. The problem - we need a way to execute an “update” in current class context. Something like execting constructor with overridden this (ie Component.prototype.constructor.call(otherThis). But we cant do it with ES6 classes.

I could see 2 ways:

  1. Use something like “sandbox”, lots of proxies, to be able to hydrate and rehydrate any function call. Ie being able to call a constructor, get parse out of a new class, and use it in a new function. Probably this is a dead end.
  2. Use babel to split constructor into lines, and then being able to rehydrate not the property result, but creation itself.

ie

const constructionLines = () => ({
  data: [],
  push(a){
    this.data.push(a);
    a();
  }
});
class SomeForm extends React.Component {
   constructor() {
      super()
      this._constructionLines =  constructionLines();
      this._constructionLines.push(() => this.state = { login: '' }))
      this._constructionLines.push(() => this.handle = handle(this))

      if (module.hot) {
         module.hot.accept('./universal-handler', () => {
            this.handle = handle(this)
         })
      }
   }

The “problem” - we should not update ALL the properties in constructor, especially state.

Calling for a babel Jedi!

@theKashey Thanks | Спасибо за оперативные ответы 😃