-1

So I'm trying to simulate the state by clicking a button. The 'before' status seems to have the correct value, but why is the 'after' not displaying the correct value even if the setState is already hit by the code?

class App extends Component {
    constructor(){
        super()
    
        this.state = {isLoggedIn: false}
        this.OnClick = this.OnClick.bind(this);
    }

OnClick(){
        
        this.setState(prev =>
            {
                return (prev.isLoggedIn = !this.state.isLoggedIn);
            })

        console.log(`After setState value: ${this.state.isLoggedInstrong text}`) // setState is done, why is this.state displaying incorrect value?
    }

render()
    {
        console.log(`Before setState value: ${this.state.isLoggedIn}`)
        return <Login isLoggedIn={this.state.isLoggedIn} OnClick={this.OnClick}  />
    }
}

import React from "react";

class Login extends React.Component
{
    render()
    {
        const {isLoggedIn, OnClick} = this.props;

        return (
        <div>
            <button onClick={OnClick} >{isLoggedIn ? "Log Out" : "Log In"} </button>
        </div>
        )
    }
}

export default Login;

OUTPUT:

"Before setState value: false" (Initial display, button value is: Log In)

When button is clicked: "After setState value: false" <------ why false when setState has been hit already? Not real-time update until Render is called?

"Before setState value: true" (Button value is now: Log Out)

Datadump
  • 45
  • 6
  • because it is **not** a **synchronous** operation – Mechanic Sep 11 '21 at 08:39
  • To trigger the rerender the new state must be a *new* object. currently you update property of an object, but its still same object, so react framework dont know that its need to be rerendred – Yosef Tukachinsky Sep 11 '21 at 08:43
  • Does this answer your question? [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – Ryan Le Sep 11 '21 at 08:43
  • Does this answer your question? [Why is setState in reactjs Async instead of Sync?](https://stackoverflow.com/questions/36085726/why-is-setstate-in-reactjs-async-instead-of-sync) – Martin Sep 11 '21 at 08:43
  • Thanks guys, lots of learning I'm getting here, cheers – Datadump Sep 11 '21 at 21:24

2 Answers2

0

The main problem I see in your code is you’re trying to mutate the state.

this.setState(prev => {
  return (prev.isLoggedIn = !this.state.isLoggedIn);
})

You have to merge to the state not mutate it. You can do it simply by returning an object like this.

this.setState((prev) => {
   return { isLoggedIn: !prev.isLoggedIn };
});

This will fix all the weird behaviours in your code.

Full Code

App.js

import { Component } from "react";
import Login from "./Login";

class App extends Component {
  constructor() {
    super();

    this.state = { isLoggedIn: false };
    this.OnClick = this.OnClick.bind(this);
  }

  OnClick() {
    this.setState((prev) => {
      return { isLoggedIn: !prev.isLoggedIn };
    });

    console.log(`After setState value: ${this.state.isLoggedIn}`);
  }

  render() {
    console.log(`Before setState value: ${this.state.isLoggedIn}`);
    return <Login isLoggedIn={this.state.isLoggedIn} OnClick={this.OnClick} />;
  }
}

export default App;

Login.js

import { Component } from "react";

class Login extends Component {
  render() {
    const { isLoggedIn, OnClick } = this.props;

    return (
      <div>
        <button onClick={OnClick}>{isLoggedIn ? "Log Out" : "Log In"} </button>
      </div>
    );
  }
}

export default Login;

CodeSandbox - https://codesandbox.io/s/setstate-is-not-update-the-state-69141369-efw46

Yushan
  • 1,029
  • 1
  • 9
  • 12
  • your codesandbox result seems right, but my console is not the same. It only becomes right when I put the "after" state inside a setTimeout() for 3 seconds which shows the asynchronous behavior suggested earlier. My console result is: Before setState value: false After setState value: false Before setState value: true After setState value: true Before setState value: false After setState value: false Before setState value: true – Datadump Sep 12 '21 at 02:46
  • @Datadump can you share a codesandbox here – Yushan Sep 12 '21 at 08:28
-1

try this

this.setState({
   isLoggedIn:!this.state.isLoggedIn
})

or

this.setState(prev => ({
   isLoggedIn:!prev.isLoggedIn
}))
Omar Khaled
  • 439
  • 1
  • 4
  • 16