2

I am new to React so sorry if this is a bad question. I am having trouble with changing the state of a child component then calling a method that changes the state of the parent component. Right now when I trigger the method that is supposed to change states the parent state changes as it should but the child state does not change at all.

When I remove the state change on the Parent component the child component changes as it should.

Here is the parent component (edited to remove code not related to the problem)

class Parent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            loginModalName: "Log in"
        }
        this.bindedUpdateModalName = this.updateModalName.bind(this)
    }

    updateModalName(name) {
        console.log(name);
        this.setState({
            loginModalName: name
        });
    };

    render() {
        return (
            <Child loginSwitch = {this.bindedUpdateModalName}/>
        );
    }
}

and here is the child component

class Child extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            isLoggingIn: true
        }
    }

    toggleLoggingIn = () => {
        this.setState({
            isLoggingIn: !this.state.isLoggingIn
        });
        console.log(this.state.isLoggingIn);
        this.props.loginSwitch(this.state.isLoggingIn ? "Log in" : "Register");
    };

    render() {
        return (
            <Button onClick={this.toggleLoggingIn} type="link" style={{paddingLeft: 0}}>register now!</Button>
        );
    }

There are no error messages, but my logs show

true
Log In

every time I click the button

Jacob Hicks
  • 55
  • 1
  • 7
  • this is because setState is async. You need to wait for the state update to happen or use a change variable to both set state and call the next function – John Ruddell May 31 '19 at 16:47
  • Issue is explained [here](https://stackoverflow.com/questions/41278385/setstate-doesnt-update-the-state-immediately) – Peter Cheng May 31 '19 at 17:04

2 Answers2

4

This is because setState is async. You need to wait for the state change to happen (second callback option for setState, or componentDidUpdate) or you can use a variable like isLogin to hold the new state value. Change your handler to this

toggleLoggingIn = () => {
    const isLogin = !this.state.isLoggingIn
    this.setState({
        isLoggingIn: isLogin
    });
    console.log(this.props.loginSwitch);
    this.props.loginSwitch(isLogin ? "Log in" : "Register");
};

Check it out in action!

The issue you were experiencing is trying to use local state on an antd form. You should either use parent state and pass as a prop, or use the antd way of updating "state" on the HOC

John Ruddell
  • 25,283
  • 6
  • 57
  • 86
3

As John mentioned setState is async. However, setState takes a second parameter that is a callback function to call after is has updated the state.

From the docs:

The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead.

Try this:

toggleLoggingIn = () => {
    const isLogin = !this.state.isLoggingIn
    this.setState(
        {
            isLoggingIn: !this.state.isLoggingIn
        },

        // Callback to run after state has been updated 
        () => {
            console.log(this.state.isLoggingIn);
            this.props.loginSwitch(this.state.isLoggingIn ? "Log in" : "Register");
        }
    );
};
Cubed Eye
  • 5,581
  • 4
  • 48
  • 64
  • Interestingly enough, using this code changes the log output to `false Register` but it still does not solve my problem as it still does not toggle between true and false – Jacob Hicks May 31 '19 at 17:03
  • 1
    @JacobHicks I've fixed the code to match you're example – Cubed Eye May 31 '19 at 17:57