Search code examples
reactjsreact-nativereduxreact-reduxredux-thunk

How to Run an Async Action Creator inside an Async Redux Thunk Action Creator?


I'm trying to figure out the correct implementation of running an Async Action Creator inside an Async Redux Thunk Action Creator. Which of the following is the correct method?

Method 1:

export const loginUser = () => async(dispatch) => {        
    try {
        user = await LoginManager.logInWithPermissions(['public_profile', 'email']);                              
        dispatch({type: LOGIN, payload: user});
        dispatch({type:SAVE_USER_TO_DEVICE, payload:saveUserToDevice(user)});                              
    } catch (error) {
        console.log(error);
    }
}

export const saveUserToDevice = async(user)=> {        
    try {
        //saveUserToDeviceStatus returns true if successful, otherwise returns an object
        const saveUserToDeviceStatus = await Keychain.setGenericPassword('user', JSON.stringify(user)); 
        return (saveUserToDeviceStatus? true: false);
    } catch (error) {
        console.log(error);
    }    
}

Method 2: saveUserToDevice dispatch itself

export const loginUser = () => async(dispatch) => {        
    try {                     
        user = await LoginManager.logInWithPermissions(['public_profile', 'email']);                      
        dispatch({type: LOGIN, payload: user});
        saveUserToDevice(user);
    } catch (error) {
        console.log(error);
    }
}

export const saveUserToDevice = (user) => async(dispatch) => {        
    try {
        //saveUserToDeviceStatus returns true if successful, otherwise returns an object
        const saveUserToDeviceStatus = await Keychain.setGenericPassword('user', JSON.stringify(user)); 
        saveUserToDeviceStatus = saveUserToDeviceStatus? true: false;
        dispatch({type: SAVE_USER_TO_DEVICE, payload: saveUserToDeviceStatus});
    } catch (error) {
        console.log(error);
    }    
}

Method 3: saveUserToDevice returns action object

export const loginUser = () => async(dispatch) => {        
    try {                     
        user = await LoginManager.logInWithPermissions(['public_profile', 'email']);                      
        dispatch({type: LOGIN, payload: user});
        const saveUserToDeviceAction = await saveUserToDevice(user);
        dispatch(saveUserToDeviceAction);
    } catch (error) {
        console.log(error);
    }
}

export const saveUserToDevice = async(user) => {        
    try {
        //saveUserToDeviceStatus returns true if successful, otherwise returns an object
        const saveUserToDeviceStatus = await Keychain.setGenericPassword('user', JSON.stringify(user)); 
        saveUserToDeviceStatus = saveUserToDeviceStatus? true: false;
       return {type: SAVE_USER_TO_DEVICE, payload: saveUserToDeviceStatus};
    } catch (error) {
        console.log(error);
    }    
}

Method 4: saveUserToDevice returns boolean, dispatch is triggered inside loginUser

export const loginUser = () => async(dispatch) => {        
    try {                     
        user = await LoginManager.logInWithPermissions(['public_profile', 'email']);                      
        dispatch({type: LOGIN, payload: user});
        const saveUserToDeviceAction = await saveUserToDevice(user);
        dispatch({type: SAVE_USER_TO_DEVICE, payload: saveUserToDeviceAction});
    } catch (error) {
        console.log(error);
    }
}

export const saveUserToDevice = async(user) => {        
    try {
        //saveUserToDeviceStatus returns true if successful, otherwise returns an object
        const saveUserToDeviceStatus = await Keychain.setGenericPassword('user', JSON.stringify(user)); 
        saveUserToDeviceStatus = saveUserToDeviceStatus? true: false;
       return saveUserToDeviceStatus;
    } catch (error) {
        console.log(error);
    }    
}

Solution

  • saveUserToDevice is an action and you need to actually dispatch the action and not just call it.

    The correct way would be

    dispatch(saveUserToDevice(user));
    

    Also saveUserToDevice must dispatch the action as

    dispatch({type: SAVE_USER_TO_DEVICE, payload: saveUserToDeviceStatus});
    

    Full Code

    export const loginUser = () => async(dispatch) => {        
        try {                     
            user = await LoginManager.logInWithPermissions(['public_profile', 'email']);                      
            dispatch({type: LOGIN, payload: user});
            dispatch(saveUserToDevice(user));
        } catch (error) {
            console.log(error);
        }
    }
    
    export const saveUserToDevice = (user) => async(dispatch) => {        
        try {
            //saveUserToDeviceStatus returns true if successful, otherwise returns an object
            const saveUserToDeviceStatus = await Keychain.setGenericPassword('user', JSON.stringify(user)); 
            saveUserToDeviceStatus = saveUserToDeviceStatus? true: false;
            dispatch({type: SAVE_USER_TO_DEVICE, payload: saveUserToDeviceStatus});
        } catch (error) {
            console.log(error);
        }    
    }
    

    As per your first solution is concerned if would work if you wait on saveUserToDevice(result)} like

    export const loginUser = () => async(dispatch) => {        
        try {
            user = await LoginManager.logInWithPermissions(['public_profile', 'email']);                              
            dispatch({type: LOGIN, payload: user});
            const data = await saveUserToDevice(result);
            dispatch({type:SAVE_USER_TO_DEVICE, payload: data});                              
        } catch (error) {
            console.log(error);
        }
    }
    
    export const saveUserToDevice = async(user)=> {        
        try {
            //saveUserToDeviceStatus returns true if successful, otherwise returns an object
            const saveUserToDeviceStatus = await Keychain.setGenericPassword('user', JSON.stringify(user)); 
            return (saveUserToDeviceStatus? true: false);
        } catch (error) {
            console.log(error);
        }    
    }
    

    However of the two approaches, its cleaner to go with the approach of making saveUserToDevice being an action and dispatch it