Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wait for an action to complete?

I'm new to react and redux and have been exploring how it is different from angular.

I'm trying to do a simple login page and am not able to achieve what I want. Once I get the username and password from a login form, I submit it like below :

  submitHandler(e){
    e.preventDefault();
    var user = {
      name : this.state.username,
      password: this.state.password
    }
    this.props.authenticateUser(user);
    browserHistory.push('/home');
    this.setState({
      username:'',
      password:''
    })
  }

Here, authenticateUser is a function inside my reducer (shown below) which checks from an already existing user's list and then the state with a property isAuthenticated :

 case 'AUTHENTICATE_USER':
        var users = state.userslist;
        var payloadUser = action.payload;
        for (let i = 0; i < users.length; i++) {
            let user = users[i]
            if (user.name === payloadUser.name) {
                return {...state, isAuthenticated: true };
            }
        };
        return {...state, isAuthenticated: false };

Now in Angular, I would just wait for this call to finish and check whether this value is true or false, and navigate to the home page using angular-router.

But how would I achieve the same in react. I am using react-router in my sample application.

I tried to check isAuthenticated in my submitHandler function right below where I call this.props.authenticateUser(user) , but the problem is , this.props.isAuthenticateUser is not updated and returns false, which I have set as the initialState in my reducer.

Please tell me how I can check isAuthenticated after the authenticateUser function has called so that I can proceed with my homepage routing. Do I have to use a promise and wait for the state to be updated by redux and then check it again ?? (I know react is a one way path and it doesn't work that way.)

Also if my authentication fails, how do I show an error panel below my login form. I think using a component for the error panel which takes in isAuthenticated as input and checking if false in order to show (ie. ngShow like in Angular.) But I'm afraid that will not work as well, since my state will not be updated just in time.

I know that the render function will be called with any change in state. But I just can't wrap my head around React clearly.

Edit: For brevity, I have not provided the action creators and the full reducer above rather shown only the switch case operation for authenticate user. My app follows the original redux architecture with actions, reducers, containers, components and such.

EDIT: Github link here

like image 634
v1shnu Avatar asked Feb 05 '17 17:02

v1shnu


Video Answer


2 Answers

I will try to explain briefly how data flow works in a Redux application:

  1. The reducer should not be called directly from the component. Your call to authenticate user should be inside an action and that is what should be dispatched from the component.

  2. When you define the action, you can give a property to define the type of action it is. For example, in this case, it could be type: AUTHENTICATE_USER

  3. After the completion of the action, the redux store calls the reducer where with the current state and the action. Here, the redux state can be updated based on action.type (the second code snippet above)

  4. This is the most important part: your component needs to have a mapStateToProps method which passes values from your Redux state as props into your component. So as the state changes, the props get updated and the component re-renders. To use mapStateToProps, you need to implement connect from the react-redux library. (https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options)

To achieve what you want, this is what I suggest:

  1. In your component's componentWillReceiveProps method, checking for props.isAuthenticated and using context.router.push() to navigate if the user is authenticated.

  2. Also having a check based on props.isAuthenticated in the component's render method to show the error message.

A few links which could be useful:

https://redux.js.org/basics/data-flow

https://redux.js.org/basics/usage-with-react

like image 89
Nupur Avatar answered Sep 30 '22 00:09

Nupur


I prefer code like this:

const mapStateToProps = (state) => {
  return {
      isAuthenticated: state.isAuthenticated
  }
}

class Dashboard extends PureComponent {
  render() {
    const { isAuthenticated } = this.props

    return (
      <div>
        { isAuthenticated ? (
          <div>
            <p>Hello</p>
          </div>
        ) : (
          Login()
        )}
      </div>
    )
  }
}

Source: https://stackoverflow.com/a/56899814/3850405

Addition to the answer from @Nupur. componentWillReceiveProps() is considered unsafe and the method name now is: UNSAFE_componentWillReceiveProps()

https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops

Use componentDidUpdate() instead if you would like this approach, this method is not called for the initial render.

componentDidUpdate(prevProps) {
    if (this.props.isAuthenticated ) {
        //perform code here
    }
}

https://reactjs.org/docs/react-component.html#componentdidupdate

like image 40
Ogglas Avatar answered Sep 30 '22 00:09

Ogglas