Search code examples
reactjsreduxredux-thunksynchronousrefresh-token

Make Redux thunk calls synchronous for refreshing tokens


Integrated redux with thunk middleware. On expiry of access token, refresh token api is called and on its success the first api which was not successful due to expired token is recalled again.

The refresh token api is being called and returned because it is async. and the edit api is called right away before the response of refresh token success. How do i make it synchronous so as to call the api only after the response of refresh token is received

export function editClothDetails(data, id) {
return  function(dispatch, getState) {
    dispatch({ type: actions.EDIT_CLOTH_REQUEST });
     fetch(BASE_URL + EDIT_CLOTH_URL + `/${id}`, {
        method: "PUT",
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + getState().Auth.accessToken
        },
        body: JSON.stringify({ ...data })
    })
    .then(result => checkHttpStatus(result))
    .then(result => checkForError(result))
    .then(jsonResponse => {
        dispatch({
            type: actions.EDIT_CLOTH_SUCCESS,
            payload: jsonResponse
        });
    })
    .catch((error) => {
        if(error.message === "Invalid token") {
            //what should be the right way to make these dispatches synchronous
            dispatch(refreshToken());
            dispatch(editClothDetails(data, id)); //setTimeout(()=> dispatch(editClothDetails(data, id)), 100);
        }
        console.error("There is an error in editing cloth details !! " + error.message);
        dispatch({
            type: actions.EDIT_CLOTH_FAILED,
            payload: error.message
        });
    });
};
}

export function refreshToken() {
    return (dispatch, getState) => {
        dispatch({ type: actions.REFRESH_TOKEN_REQUEST });
        fetch(BASE_URL + '/token', {
            method: "GET",
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json',
                'authorization': 'Bearer ' + getState().Auth.refreshToken
            },
        })
        .then(result => checkHttpStatus(result))
        .then(result => checkForError(result))
        .then(jsonResponse => {
            storeLocally(constants.APP_NAME, jsonResponse);
            dispatch({
                type: actions.REFRESH_TOKEN_REQUEST_SUCCESS,
                payload: jsonResponse
            });
        })
        .catch((err) => {
            console.error("There is an error refreshing token !!" + err.message);
            dispatch({
                type: actions.REFRESH_TOKEN_REQUEST_FAILED,
                payload: err.message
            });
        });
    };
}

Solution

  • You have to use async-await here ...

    export function editClothDetails(data, id) {
    return  async function(dispatch, getState) {      // -> see here
    
        .catch((error) => {
            if(error.message === "Invalid token") {
                await dispatch(refreshToken());                 //--> see here
                dispatch(editClothDetails(data, id)); 
            }
     
     // your other code
          
    };
    }
    
    export async function refreshToken() {.  /// ---> see here
        return async (dispatch, getState) => {
            dispatch({ type: actions.REFRESH_TOKEN_REQUEST });
            /// your other code
        };
    }