0

enter image description hereI wrote the next code:

class Data extends React.Component {
  constructor(props) {
    super(props);
    this.state = { checked: true };
  }
  changeF() {
    this.setState({ checked: !this.state.checked });
  }
  render() {
    var message;
    if (this.state.checked) {
      message = "checked";
    } else {
      message = "UNchecked";
    }
    return (
      <div>
        <p> i: {message}</p>
        <input
          type="checkbox"
          onChange={this.changeF}
          defaultChecked={this.state.checked}
        />
      </div>
    );
  }
}
var doc = document.getElementById("content");
ReactDOM.render(<Data />, doc);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

It should display "checked" when i will check the checkbox and "unchecked if not". Who can explain, why the app is not working?

1
  • You need to change the state using a callback. Like this: this.setState((prevState) => ({ checked: !prevState.checked }) ). Also change onChange={this.changeF} to onChange={this.changeF.bind(this)} Commented Jul 26, 2019 at 7:57

1 Answer 1

1

You need checked not defaultChecked:

 <input type="checkbox" onChange={this.changeF} checked={this.state.checked} />

Also when defining a method/function in a React class-component, you should be cognizant of the this keyword. In the changeF function you defined above, you are trying to update state by using this.setState(). However, if you actually tried to console.log(this) within changeF, you will get undefined, so naturally you cannot use this.setState()

Within changeF we need to point the this keyword to your component somehow, either by using an arrow-function which has lexical-binding, OR by explicitly binding the this keyword belonging to the changeF function inside the component's constructor.

Try turning changeF into an arrow-function, which will implicitly bind the this keyword to your component's execution context:

  changeF = () => {
    this.setState({ checked: !this.state.checked });
  }

See sandbox for working example: https://codesandbox.io/s/prod-http-t480z

Ironically, now that you have a practical answer for your code, you probably now have more questions about this. It is arguably the most confusing topic in JavaScript.

Here's a cheater's guide to understand the this keyword.

Note: this almost always refers to nearest object that is a level above it/owns it.

Let's go through a couple of examples using your console.

  1. Printing this

    Code: console.log(this)

    Result: Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

There is a global window object, and when you simply print this without any wrapping objects, it points to the nearest one.

  1. Printing this within a function()

    Code:

    function printSomething(){
            console.log(this)
        }
    
        printSomething()
    

    Result: Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

Still, this points to the nearest object. The window.

  1. Printing this within a function() that is inside an object

    Code:

    function nestedThis(){
            let myObj = {
               getThis: function(){
                   console.log(this)
               }
            }
            return myObj
        }
    
        nestedThis().getThis()
    

    Result: { getThis: ƒ}

Nice! We aren't getting the window object anymore. Instead, now this refers to myObj, which is the object that owns it.

Now to understand why your changeF function did not work:

  changeF() {
    this.setState({ checked: !this.state.checked });
  }

In the context of a React component, the this keyword is not immediately given a value. It will not inherently receive the window object, hence React component's have their own execution scope. And since it cannot find an object at a level above it, in a sense this becomes "lost".

As a workaround, you will see people binding their traditional functions so that the this keyword points to the component context.

  constructor(props) {
    super(props);
    this.state = { checked: true };

    this.changeF = this.changeF.bind(this)
  }

Note: Within the constructor, the this keyword is defined and you have access to all the class properties.

But why the heck do arrow-functions () => {} work? Well arrow-functions have something called lexical-scoping. Which essentially just means that when you call this inside the function, it will point to the overarching execution context.

Sign up to request clarification or add additional context in comments.

5 Comments

@Asking sorry about that. See my solution above and also working sandbox :)
the code doesn't work, even if i inserted all your changes
Seems to be working fine in this sandbox: codesandbox.io/s/prod-http-t480z @Asking
when i run the code on my browser i get the error from the image above
@Asking that is just weird, alternatively you can just bind the function to your constructor. See the last bit of code I wrote towards the end about constructor.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.