Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redux thunk: how to await completion of an async action

As redux thunk calls functions returned by action creators asynchronously, how I can ensure after calling the action creator that redux has actually dispatched the action before moving on?

I need to fetch CSRF token before each POST request to server, and for both procedure there's an corresponding action.

The problem is, if I call those action creators successively, POST action is for some reason getting dispatched before CSRF action is dispatched. I want to keep these concerns separated, so I don't want to combine the actions.

How I can synchronize the action creator calling code with redux thunk dispatching those actions?

like image 280
Tuomas Toivonen Avatar asked Jun 22 '16 15:06

Tuomas Toivonen


People also ask

How do you dispatch async thunk action?

To dispatch async actions into our store, we have to apply the thunk middleware by writing: const store = createStore(joke, applyMiddleware(thunk)); to apply the middleware. Then in App , we call dispatch with the function returned from the fetchJoke passed inside.

How do you handle async actions in Redux?

Redux Async Data Flow​ Just like with a normal action, we first need to handle a user event in the application, such as a click on a button. Then, we call dispatch() , and pass in something, whether it be a plain action object, a function, or some other value that a middleware can look for.

How do you dispatch actions in redux-thunk?

Redux Thunk is a middleware that lets you call action creators that return a function instead of an action object. That function receives the store's dispatch method, which is then used to dispatch regular synchronous actions inside the function's body once the asynchronous operations have been completed.

Can I use await with dispatch?

The dispatched action updateDone mutates state before post and profile data are in the store. This makes async/await unpredictable since we don't know when a dispatch with response data executes. We can wait for a response via await inside the thunk itself but lose all control outside of the function.


1 Answers

You can make thunk action creator as Promise, make easier to control async jobs.

export function createXHRAction(xhrType, dispatch, options) {
    // you can customize createXHRAction here with options parameter.

    dispatch({ type: xhrType, data: { fetching: true, data: [] });

    return new Promise( (resolve, reject) => {
        fetch(options.url, { ... })
        .then( (response) => {
            // to getting server response, you must use .json() method and this is promise object
            let parseJSONPromise = response.json();

            if(response.status >= 200 && response.status < 300) {
                parseJSONPromise.then( (result) => {
                    dispatch({ type: xhrType, data: { fetching: false, data: result.data });
                    resolve(result.data);
                });
                return parseJSONPromise;    // make possible to use then where calling this
            }
            else {
                return parseJSONPromise.then( res => {
                    reject({ message: res.error.message });
                });
            }
        })
        .catch( (error) => {
            // handles common XHR error here
        });
    });
}

now you can easily create new XHR actions like this:

import { createXHRAction } from './actions';

export function getUser(id) {
    return (dispatch) => {
        return createXHRAction('user', dispatch, {
            method: 'get',
            url: `/user/${id}`
        });
    };
}

now you can use thunk action like synchronous:

import { dispatch } from './store';
import { getUser } from './action/user';

class SomeComponent extends React.Component {
    ...
    loadData(id) {

        // from here, store's state should be { fetching: true, data: [] }
        dispatch(getUser(id))
        .then( (userData) => {
            // now from here, you can get the value from parameter or you can get it from store or component props if super component passing it by redux provider.
            // store state should be { fetching: false: data [..., ...] }
            // do something with received data
        })
        .catch( (error) => {
        }));

    }
}
like image 155
modernator Avatar answered Sep 21 '22 12:09

modernator