Search code examples
reactjsreduxreact-reduxredux-saga

React with Redux Saga - How to get data back from axios call


I'm workign with React + Redux + Saga and what I have done so far works perfectly but I need to send data back after a (succeful call).

dispatch to route => sends back an ID => gets the ID

But it doesn't do it.

Route (express server):

axios.post( ... 

return res.json(resAlbum);

My saga :

  function* postSpotifyAlbum(obj) {
      obj = {
        method: "POST",
        url: BASEurl,
        params: {
          token: obj.payload.token,
          albumCode: obj.payload.albumCode,
          albumGenres: obj.payload.albumGenres,
          userCode: obj.payload.userCode,
        },
      };
    
      try {
        const spotifyCategories = yield call(axios, axiosCall(obj));
        yield put({
          type: "POST_SPOTIFY_ALBUMS_SUCCESS",
          payload: JSON.parse(spotifyCategories.data),
        });
        return spotifyCategories.data;
      } catch (error) {
        console.log(error);
        yield put({ type: "POST_SPOTIFY_ALBUMS_ERROR", payload: error });
      }
    } 

Component:

const res = await dispatch(postSpotifyAlbums(obj));

if I console log the res I just get back Obj I just sent.

Question:

Is there any way to send something back from SAGA?

i.e. res gives me just inserted obj

Or there's no way so then I need to make a second call to the reducer to give me back the just inserted obj?

I repeat, everythig wortks perfectly from the dispatch till the reducer (I ommited to be shorter) I just would like to see if I can expand my SAGA knowledge


Solution

  • With Saga

    The returned value from calling dispatch is just the action that you dispatched. Your saga will "take" this action and dispatch additional actions, but that is all considered independent. So your res variable is just the original action postSpotifyAlbums(obj).

    Or there's no way so then I need to make a second call to the reducer to give me back the just inserted obj?

    Yes, you would need to have a useSelector in the component that is listening for the posted value.


    With Thunk

    This is a lot easier to do with because the thunk middleware overrides the default behavior of dispatch. When you dispatch a thunk (an action creator which is a function of dispatch) then you get back the value that's returned by the thunk.

    Any return value from the inner function will be available as the return value of dispatch itself. This is convenient for orchestrating an asynchronous control flow with thunk action creators dispatching each other and returning Promises to wait for each other's completion - docs

    action

    const postSpotifyAlbums = (payload) => async (dispatch) => {
      dispatch({ type: "POST_SPOTIFY_ALBUMS_PENDING" });
      const args = {
        method: "POST",
        url: BASEurl,
        params: {
          token: payload.token,
          albumCode: payload.albumCode,
          albumGenres: payload.albumGenres,
          userCode: payload.userCode
        }
      };
      try {
        const spotifyCategories = await axios.request(args);
        dispatch({
          type: "POST_SPOTIFY_ALBUMS_SUCCESS",
          payload: spotifyCategories.data
        });
        // we can customize the returned value here to anything!
        return spotifyCategories.data;
      } catch (error) {
        console.log(error);
        dispatch({ type: "POST_SPOTIFY_ALBUMS_ERROR", payload: error });
      }
    };
    

    component

    const doDispatch = async () => {
      // we get back whatever we specifically returned in the thunk
      const data = await dispatch(postSpotifyAlbums(obj));
      console.log("result", data);
      // we didn't return anything in the error case so it would be undefined
      if (data) {
        // do something with success
      }
    };
    

    With createAsyncThunk

    includes a helper function createAsyncThunk that automatically creates "pending" "rejected" and "fulfilled" actions in one step. The return value here is the final action that was dispatched.

    When dispatched, the thunk will ... return a fulfilled promise containing the final dispatched action (either the fulfilled or rejected action object) - docs

    action

    const postSpotifyAlbums = createAsyncThunk(
      "POST_SPOTIFY_ALBUMS",
      async (payload) => {
        const args = {
          method: "POST",
          url: BASEurl,
          params: {
            token: payload.token,
            albumCode: payload.albumCode,
            albumGenres: payload.albumGenres,
            userCode: payload.userCode
          }
        };
        const spotifyCategories = await axios.request(args);
        return spotifyCategories.data;
      }
    );
    

    component

    const doDispatch = async () => {
      const action = await dispatch(postSpotifyAlbums(obj));
      console.log("result", action);
      // we could have the success or the failure action
      if ( action.type === postSpotifyAlbums.fulfilled.type) {
        // do something with success
        const data = action.payload;
      } else {
        // do something with error
        const error = action.error;
      }
    };