0

I've read this post: React setState not Updating Immediately

and realized that setState is async and may require a second arg as a function to deal with the new state.

Now I have a checkbox

class CheckBox extends Component {
    constructor() {
        super();
        this.state = {
            isChecked: false,
            checkedList: []
        };
        this.handleChecked = this.handleChecked.bind(this);
    }

    handleChecked () {
        this.setState({isChecked: !this.state.isChecked}, this.props.handler(this.props.txt));
    }

    render () {
        return (
            <div>
                <input type="checkbox" onChange={this.handleChecked} />
                {`   ${this.props.txt}`}
            </div>
            )
    }
}

And is being used by another app

class AppList extends Component {
    constructor() {
        super();
        this.state = {
            checked: [],
            apps: []
        };
        this.handleChecked = this.handleChecked.bind(this);
        this.handleDeleteKey = this.handleDeleteKey.bind(this);
    }
    handleChecked(client_id) {
        if (!this.state.checked.includes(client_id)) {
            let new_apps = this.state.apps;
            if (new_apps.includes(client_id)) {
                new_apps = new_apps.filter(m => {
                    return (m !== client_id);
                });
            } else {
                new_apps.push(client_id);
            }
            console.log('new apps', new_apps);
            this.setState({apps: new_apps});
            // this.setState({checked: [...checked_key, client_id]});
            console.log(this.state);
        }
    }
    render () {
        const apps = this.props.apps.map((app) =>
            <CheckBox key={app.client_id} txt={app.client_id} handler={this.handleChecked}/>
        );

        return (
            <div>
                <h4>Client Key List:</h4>
                {this.props.apps.length > 0 ? <ul>{apps}</ul> : <p>No Key</p>}
            </div> 
        );
    }


}

So every time the checkbox status changes, I update the this.state.apps in AppList

when I console.log new_apps, everything works accordingly, but console.log(this.state) shows that the state is not updated immediately, which is expected. What I need to know is how I can ensure the state is updated when I need to do further actions (like register all these selected strings or something)

3
  • Function this.setState() is async! but as second argument you can invoke a callback, try with this.setState({ apps: new_apps }, _ => console.log(this.state) Commented Sep 7, 2018 at 15:47
  • This is well covered in the react docs, and the answers in the question you linked to. Commented Sep 7, 2018 at 15:50
  • @meagar I couldn't understand why my console.log wasn't outputing the right answer even after I used a callback function in checkbox. That's why this post Commented Sep 7, 2018 at 15:52

3 Answers 3

5

setState enables you to make a callback function after you set the state so you can get the real state

this.setState({stateYouWant}, () => console.log(this.state.stateYouWant))

in your case:

this.setState({apps: new_apps}, () => console.log(this.state))
Sign up to request clarification or add additional context in comments.

4 Comments

so what you're saying is that the state is actually changed successfully. I'm just console.loging at the wrong place?
What i'm saying is both execute at the same time, so console.log has the old state, it will always be one state behind if you get what i mean, by doing this we only log after the state was changed (callback is usually a function that is called after a certain operation was complete, in this case the setState)
If it fixed your problem choose a correct answer so other people that see this can resolve it easily
I'll do it once I can. stackoverflow doesn't allow correct answer to be chosen within 5 mins
2

The others have the right answer regarding the setState callback, but I would also suggest making CheckBox stateless and pass isChecked from MyApp as a prop. This way you're only keeping one record of whether the item is checked, and don't need to synchronise between the two.

2 Comments

just curious, right now I'm setting isChecked to be !isChecked when checkbox changes status. If I process it as a prop, how do I keep track of this status?
You have to create a function that modifies the state and pass via props to the other component and then call it there
2

Actually there shouldn't be two states keeping the same thing. Instead, the checkbox should be stateless, the state should only be kept at the AppList and then passed down:

const CheckBox = ({ text, checked, onChange }) => 
        (<span><input type="checkbox" checked={checked} onChange={() => onChange(text)} />{text}</span>);
        
class AppList extends React.Component {
  constructor() {
    super();
    this.state = { 
      apps: [
        {name: "One", checked: false },
        { name: "Two", checked: false }
      ], 
    };
  }
  
  onChange(app) {
    this.setState(
      previous => ({ 
         apps: previous.apps.map(({ name, checked }) => ({ name, checked: checked !== (name === app) })),
      }),
      () => console.log(this.state)
    );
  }
  
  render() {
    return <div>
     {this.state.apps.map(({ name, checked }) => (<CheckBox text={name} checked={checked} onChange={this.onChange.bind(this)} />))}
    </div>;
  }
}

ReactDOM.render(<AppList />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

4 Comments

In general I like the approach you suggested, but the xor thing is overly clever (clever code is bad code). I'd suggest rewriting it to be more straightforward.
@jeff I'm not sure if a nested comparison is better ... what would you suggest for "straightforward" ?
I get input is a void element tag and must neither have children` nor use dangerouslySetInnerHTML.` as an error. Something in <input> creating this problem?
@JonasWilms I would use a ternary. checked: name === app ? !checked : checked. That is, if this is the app we want to toggle, then toggle, else leave as is. I think this is more clear and obvious to future readers, and less likely to be screwed up by the writer.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.