Abdallah Alsharmani
Abdallah Alsharmani

Reputation: 49

How to wait for action to complete before redirecting in react redux?

I'm trying to log users in based on a isLoggedin state from redux store. My Login Component dispatches the fetchLogin action from the userLogin action mapped to props.

const Login = (props) => {
    const [userLogin, setUserLogin] = useState({ email: "", password: "" })

    const getValue = e => {
        e.preventDefault();
        setUserLogin({
            ...userLogin,
            [e.target.name]: e.target.value 
        });
    }

    const makeRequest = (e) => {
        e.preventDefault()
        props.userLogin(userLogin)

        if (props.isLoggedin === true) {
            console.log(Cookie.get('accessToken'))
            props.history.push('/dashboard')
        }
    }

    return (
        <Fragment>
            <Form onSubmit={makeRequest}>
                <Form.Group controlId="formBasicEmail">
                    <Form.Label>Email Address</Form.Label>
                    <Form.Control
                        type="email"
                        name="email"
                        placeholder="Enter email"
                        onChange={getValue}
                    >
                    </Form.Control>
                </Form.Group>
                <Form.Group controlId="formBasicPassword">
                    <Form.Label>Password</Form.Label>
                    <Form.Control
                        type="password"
                        name="password"
                        placeholder="Enter password"
                        onChange={getValue}
                    >
                    </Form.Control>

                </Form.Group>
                <Button
                    variant="primary"
                    type="submit"
                >
                    Login
            </Button>
            </Form>
        </Fragment>
    )
}

const mapStateToProps = (state) => ({
    isFetching: state.userReducer.isFetching,
    isLoggedin: state.userReducer.isLoggedin,
    isError: state.userReducer.isError,
    errMessage: state.userReducer.errMessage
});

const mapDispatchToProps = dispatch => ({
    userLogin: (userLogin) => {
        dispatch(fetchLogin(userLogin))
    }
});

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(Login)


The problem here is when the user clicks login, the redux action I have which takes care of logging the user in from the backend takes time and the store isn't updated fast enough for the userLogin function to confirm that the user is logged in. I understand this has something to do with promises but I'm not sure how to do it exactly.

You can find my actions below, the only action that matters is the fetchLogin which is the one being dispatched from the login component.

export const userLoginRequest = () => {
    return {
        type: USER_LOGIN_REQUEST,
        payload: {
            isFetching: true
        }
    };
}

export const userLoginSuccess = (user) => {
    return {
        type: USER_LOGIN_SUCCESS,
        payload: {
            user: {
                ...user
            },
            isLoggedin: true,
            isFetching: false
        }
    };
}

export const userLoginFailed = () => {
    return {
        type: USER_LOGIN_FAILED,
        payload: {
            isFetching: false,
            isError: true
        }
    };
}

export const fetchLogin = (userLogin) => dispatch => {
    dispatch(userLoginRequest())
    axios.post('/login', {
        email: userLogin.email,
        password: userLogin.password
    })
        .then(response => {
            if (response.status === 200) {
             dispatch(userLoginSuccess(response.data))
            }
        }, err => {
            console.log("Error", err)
         dispatch(userLoginFailed())
    })
}

Upvotes: 1

Views: 2585

Answers (1)

Clarity
Clarity

Reputation: 10873

You can return the promise from your action and set isLoggedin state after it's resolved:

export const fetchLogin = (userLogin) => dispatch => {
  dispatch(userLoginRequest())
  return axios.post('/login', {
      email: userLogin.email,
      password: userLogin.password
    })
    .then(response => {
      if (response.status === 200) {
        dispatch(userLoginSuccess(response.data))
      }
      return response; 
    }).catch(err => {
      console.log("Error", err)
      dispatch(userLoginFailed())
    })
}

// ...

 const makeRequest = (e) => {
   e.preventDefault()
   props.userLogin(userLogin).then(resp => {
     if (props.isLoggedin === true) {
       console.log(Cookie.get('accessToken'))
       props.history.push('/dashboard')
     }
   })
 }

Also for this to work you need to update mapDispatchToProps to actually return the dispatch:

const mapDispatchToProps = dispatch => ({
    userLogin: (userLogin) => dispatch(fetchLogin(userLogin))
});

One more thing, I just realised that to properly redirect in this case using the promise from dispatch is actually not necessary since it's possible to track the isLoggedin prop in useEffect

 const makeRequest = (e) => {
  e.preventDefault()
  props.userLogin(userLogin)
}

useEffect(() => {
  if (props.isLoggedin === true) {
    console.log(Cookie.get('accessToken'))
    props.history.push('/dashboard')
  }
}, [props.isLoggedin])

Upvotes: 2

Related Questions