Andrew Junior Howard Andrew Junior Howard - 17 days ago 11
Javascript Question

Redux async actions based on login

I have the following action in my actions.js file. But I get the error "Actions must be plain objects. Use custom middleware for async actions." I'm assuming it's because I putting the return in a weird place. Any ideas?

export function loginLookUp(credentials) {
const request = axios.post(`${LOGINLOOKUP_URL}`, credentials).then(function (response) {

return {
type: LOGIN_SUCCESS,
payload: true
};


})
.catch(function (error) {

return {
type: LOGIN_FAIL,
payload: false
};


});


}

Answer

Your action creator doesn't return anything - your return statements are in the scope of the promise callbacks, not the surrounding function. But that's kind of beside the point - even if you did return the promise from the action creator, Redux wouldn't know what to do with it!

In order to do asynchronous actions, you need to use a library such as redux-thunk or redux-promise. I'm not going to go into detail on how to set these libraries up, as the READMEs on their repos do a much better job, but here's a few examples of how you'd use them.

redux-thunk allows you to dispatch a function that in turn has access to dispatch, like so:

export function loginLookUp(credentials) {
    // This would look a lot cleaner if you used an ES2015 arrow function
    return function (dispatch) {
        const request = axios.post(`${LOGINLOOKUP_URL}`, credentials).then(function (response) {
            dispatch({
                type: LOGIN_SUCCESS,
                payload: true
            });
         })
         .catch(function (error) {
              dispatch({
                  type: LOGIN_FAIL,
                  payload: false
              });
         });
    }
}

This is about as simple as async actions get, and it's how the official Redux tutorial will teach you to do it - make sure to give those chapters a read, it's really helpful stuff!

redux-promise, on the other hand, lets you dispatch actions with a promise as the payload:

export function loginLookUp(credentials) {
    return {
        type: LOGIN,
        // Payload must be a promise!
        payload: axios.post(`${LOGINLOOKUP_URL}`, credentials)
    };
}

Rather than the action immediately being passed to the reducer, it will use .then() to wait for the promise in the payload to complete. Then, it will dispatch the action in one of two forms.

If the promise resolves, the action will be dispatched with the payload set to the resolved value, like so:

{
    type: LOGIN,
    payload: // The response from the server
}

If the promise fails, the action will be dispatched with the payload set to the rejected value and the error property set to true, like so:

{
    type: LOGIN,
    payload: // The error object,
    error: true
}

These are by no means the only ways of doing things - there's countless async action libraries, so if these both made you recoil in horror, there's bound to be something else that will suit you (I hear really good things from people smarter than me about redux-saga, but I can't comprehend it myself)!

Talking of sagas, this answer ended up being way longer than I intended. Hopefully it clears stuff up for you!

Comments