Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

componentWillReceiveProps state is different from render state after redux state update

First of all, all the relevant code (click on the filename for the full source code of that file).

LoginView.js

LoginView = class extends React.Component {
    handleLogin = (email, password) => {
        this.props.authenticationActionCreator.login(email, password);
    };

    componentWillMount () {
        console.log('componentWillMount', 'this.props.isAuthenticated', this.props.isAuthenticated);
    }

    componentWillReceiveProps () {
        console.log('componentWillReceiveProps', 'this.props.isAuthenticated', this.props.isAuthenticated);
    }

    render () {
        let {
            errorMessage,
            isAuthenticating
        } = this.props;

        return <div>
            <p>this.props.isAuthenticated: {this.props.isAuthenticated ? 'true' : 'false'}</p>
            <button onClick={() => {
                this.handleLogin('[email protected]', 'nosyte');
            }}>Login</button>
        </div>;
    }
};

authentication.js (reducer)

if (action.type === 'AUTHENTICATION.LOGIN_SUCCESS') {
    return initialState.merge({
        isAuthenticated: true,
        token: action.data.token,
        user: action.data.user
    });
}

authenticationActionCreator.js

authenticationActionCreator.loginSuccess = (token) => {
    let decodedToken;

    // @todo Handle failure to decode token.

    decodedToken = jwtDecode(token);

    localStorage.setItem('token', token);

    return {
        type: 'AUTHENTICATION.LOGIN_SUCCESS',
        data: {
            token,
            user: decodedToken.user
        }
    };
};

The flow is simple:

  1. User opens the page.
  2. User clicks the <button /> that invokes authenticationActionCreator.login.

The console.log output is:

componentWillMount this.props.isAuthenticated true
action AUTHENTICATION.LOGIN_REQUEST @ 16:52:50.880
componentWillReceiveProps this.props.isAuthenticated true
componentWillReceiveProps this.props.isAuthenticated false
action AUTHENTICATION.LOGIN_SUCCESS @ 16:52:51.975

The expected console.log output is:

componentWillMount this.props.isAuthenticated true
action AUTHENTICATION.LOGIN_REQUEST @ 16:52:50.880
componentWillReceiveProps this.props.isAuthenticated false
action AUTHENTICATION.LOGIN_SUCCESS @ 16:52:51.975
componentWillReceiveProps this.props.isAuthenticated true

The problem is that render has the correct state (the state after AUTHENTICATION.LOGIN_SUCCESS) and componentWillReceiveProps has the old state (the state after AUTHENTICATION.LOGIN_REQUEST).

I am the last call to componentWillReceiveProps to have the same state object as the render method.

Is this:

  • a bug
  • I am doing something wrong
  • my expectations are false

?

like image 728
Gajus Avatar asked Jan 17 '16 17:01

Gajus


People also ask

Is there any need to use componentWillReceiveProps?

ReactJS – componentWillReceiveProps() MethodThis method is used during the updating phase of the React lifecycle. This function is generally called if the props passed to the component change. It is used to update the state in response with the new received props.

What is componentWillReceiveProps?

The componentWillReceiveProps() is invoked before our mounted React component receives new props. It is called during the updating phase of the React Life-cycle. It is used to update the state in response to some changes in our props.


1 Answers

It took me writing all this debug trace/question to remember that componentWillReceiveProps API is:

componentWillReceiveProps: function(nextProps) {}

In other words, my LoginView.js example should have been:

LoginView = class extends React.Component {
    handleLogin = (email, password) => {
        this.props.authenticationActionCreator.login(email, password);
    };

    componentWillReceiveProps (nextProps) {
        console.log('componentWillReceiveProps', 'nextProps.isAuthenticated', nextProps.isAuthenticated);
    }

    render () {
        let {
            errorMessage,
            isAuthenticating
        } = this.props;

        return <div>
            <p>this.props.isAuthenticated: {this.props.isAuthenticated ? 'true' : 'false'}</p>
            <button onClick={() => {
                this.handleLogin('[email protected]', 'nosyte');
            }}>Login</button>
        </div>;
    }
};
like image 168
Gajus Avatar answered Dec 06 '22 17:12

Gajus