0

I was trying to render simple react application with web-pack, all is compiles perfectly. On runtime code fails with following stacktrace:

Uncaught TypeError: Cannot read property 'setState' of undefined
at checkBoxCheck (App.js:174)...

Here is the code with the setState:

//App.js
export class Login extends Component {
  constructor(props) {
    super(props);
    this.state = { remember: false };
  }

  checkBoxCheck(event) {
    this.setState({
      remember: !this.state.remember
    });
    console.log(this.state.remember);
  }

  render() {
    let msg = this.state.remember ? "checked" : "uncheked";
    return (
      <form method="post">
        <h1>{msg}</h1>
        <label htmlFor="login">Login: </label>
        <input type="text" name="login" />
        <label htmlFor="pass"> Password: </label>
        <input type="password" name="pass" />
        <label htmlFor="remember">Remeber me: </label>
        <input
          type="checkbox"
          name="remember"
          defaultChecked={this.state.remember}
          onChange={this.checkBoxCheck}
        />
      </form>
    );
  }
}

Where is syntax issue?

Also i were trying to make an checkBoxCheck 'arrow' function, that cause compiling errors:

    checkBoxCheck = event => {
    this.setState((prevState, props) =>({
        remember: !prevState.remember
    }));
    console.log(this.state.remember);
}

//output        
SyntaxError: D:/Projects/Learning-projects/learn-react/src/App.js: Unexpected token (53:18)

  51 |     }
  52 |
> 53 |     checkBoxCheck = event => {
     |                   ^
  54 |         this.setState((prevState, props) =>({
  55 |             remember: !prevState.remember
  56 |         }));
wtsiamruk
  • 339
  • 3
  • 16
  • 2
    Possible duplicate of [How to access the correct \`this\` inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – CRice Jul 09 '18 at 22:33
  • Webpack doesn't actually *execute* all of your code. If you want to find issues before deployment, write *tests*. – jonrsharpe Jul 09 '18 at 22:38
  • 1
    Try e.g. binding your `checkBoxCheck` method to `this` in the constructor. `this.checkBoxCheck = this.checkBoxCheck.bind(this);` – Tholle Jul 09 '18 at 22:39
  • @jonrsharpe i din't get to that stage in JavaScript yet:) – wtsiamruk Jul 10 '18 at 09:35

2 Answers2

3

You actually forgot to bind context to the checkBoxCheck method.

...
<input
   type="checkbox"
   name="remember"
   defaultChecked={this.state.remember}
   onChange={this.checkBoxCheck.bind(this)} // <- .bind(this)
/>
...

Tips:

There are little remarks regards your code:

  1. Do not bind context in render method.
  2. Do not use this.state within a this.setState function

Never .bind() in render

render() { // <- If you here
  ...
  <input
    ...
    onChange={this.checkBoxCheck.bind(this)} // <- don't do this
  />
  ...
}

By binding your event handlers like this, you create brand new functions every time React calls your component’s render. Instead, you can convert your function into the arrow function:

checkBoxCheck = event => {
  this.setState({
    remember: !this.state.remember
  });
  console.log(this.state.remember);
}

and call it like this:

...
<input
   type="checkbox"
   name="remember"
   defaultChecked={this.state.remember}
   onChange={this.checkBoxCheck} // <- there is no binding
/>
...

Also, you can bind all your methods inside the class constructor:

//App.js
export class Login extends Component {
  constructor(props) {
    super(props);
    this.state = { remember: false };

    this.checkBoxCheck = this.checkBoxCheck.bind(this); // <- Bind your methods here
  }
...

and call your function without binding:

...
<input
   type="checkbox"
   name="remember"
   defaultChecked={this.state.remember}
   onChange={this.checkBoxCheck} // <- there is no binding
/>
...

Using this.state within a this.setState

An example can be an increment function:

function increment() {
  this.setState({value: this.state.value + 1});
}

If these two setState operations is grouped together in a batch it will look be something like the following, given that value is 1:

setState({value: 1 + 1})
setState({value: 1 + 1})

This can be avoided with using callbacks which takes the previous state as the first argument:

function increment() {
  this.setState(prevState => ({value: prevState.value + 1}));
}

Then react will call the argument with the correct and updated state, even when things happen in batches. And the example above will be something like:

setState({value: 1 + 1})
setState({value: 2 + 1})

Refs:

Roman Mahotskyi
  • 4,576
  • 5
  • 35
  • 68
  • also i want to mention that if i use the solution with arrow function i have a compilation error :\ ERROR in ./src/App.js Module build failed (from ./node_modules/babel-loader/lib/index.js): SyntaxError: D:/Projects/Learning-projects/learn-react/src/App.js: Unexpected token (52:18) 50 | } 51 | > 52 | checkBoxCheck = event => { | ^ 53 | this.setState({ 54 | remember: !state.remember 55 | }); – wtsiamruk Jul 10 '18 at 08:37
  • @WaldemarTsiamruk, you can try to use this plugin to enable this feature: https://babeljs.io/docs/en/babel-plugin-transform-class-properties/ – Roman Mahotskyi Jul 10 '18 at 16:01
1

This is happening because you need to bind this.checkBoxCheck to this.

Add this: this.checkBoxCheck = this.checkBoxCheck.bind(this) to your constructor.

Example:

//App.js
export class Login extends Component {
  constructor(props) {
    super(props);
    this.state = { remember: false };

    this.checkBoxCheck = this.checkBoxCheck.bind(this);
  }

  checkBoxCheck(event) {
    this.setState({
      remember: !this.state.remember
    });
    console.log(this.state.remember);
  }

  render() {
    let msg = this.state.remember ? "checked" : "uncheked";
    return (
      <form method="post">
        <h1>{msg}</h1>
        <label htmlFor="login">Login: </label>
        <input type="text" name="login" />
        <label htmlFor="pass"> Password: </label> 
        <input type="password" name="pass" />
        <label htmlFor="remember">Remeber me: </label>
        <input
          type="checkbox"
          name="remember"
          defaultChecked={this.state.remember}
          onChange={this.checkBoxCheck}
        />
      </form>
    );
  }
}
NoxelNyx
  • 995
  • 1
  • 7
  • 21