React with Redux Saga - 如何从 axios 调用中获取数据

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

我正在使用 React + Redux + Saga 工作,到目前为止我所做的工作非常完美,但我需要在(成功调用)后发回数据。

调度到路由 => 发回一个 ID => 获取 ID

但它并没有做到。

路由(快递服务器):

axios.post( ... 

return res.json(resAlbum);

我的传奇:

  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 });
      }
    } 

分量:

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

如果我控制台记录 res,我会返回我刚刚发送的 Obj。

问题:

有什么办法可以从 SAGA 寄回东西吗?

即res 给我刚刚插入的 obj

或者没有办法,那么我需要再次调用reducer把刚刚插入的obj还给我?

我再说一遍,从 dispatch 到 reducer 的一切都非常完美(我省略了更短的部分)我只是想看看我是否可以扩展我的 SAGA 知识

与佐贺

调用 dispatch 的 returned 值就是您发送的操作。您的 saga 将“执行”此操作并发送其他操作,但这都被认为是独立的。所以你的 res 变量只是原始操作 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?

是的,您需要在监听发布值的组件中有一个 useSelector


有 Thunk

这在 中要容易得多,因为 thunk 中间件会覆盖 dispatch 的默认行为。当您发送一个 thunk(一个动作创建者,它是 dispatch 的函数)时,您会取回由 thunk return 编辑的值。

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

行动

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 });
  }
};

组件

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
  }
};

使用 createAsyncThunk

includes a helper function createAsyncThunk 一步自动创建“待处理”、“拒绝”和“已完成”操作。这里的 return 值是最后派发的操作。

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

行动

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;
  }
);

组件

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;
  }
};

不确定您是否仍在寻找解决方案,但这是我的代码实现。 流程是这样的。

  1. 我们从我们的组件发出调度请求
  2. redux 调度名为“INITIATE_TRANSACTION”的操作。
  3. redux saga 拦截动作并开始api 调用
  4. API 调用成功,“INITIATE_TRANSACTION_SUCCESS”与 api 的值一起发送。
  5. 现在 Saga returns 从 axios 到调用步骤 1 分派的实际调用函数的值。

这是代码。

// store.js

import {middleware as thunkMiddleware} from 'redux-saga-thunk';
const middlewares = [thunkMiddleware];
const sagaMiddleware = createSagaMiddleware();
middlewares.push(sagaMiddleware);
if (process.env.NODE_ENV === `development`) {
  const {logger} = require(`redux-logger`);

  middlewares.push(logger);
}

const persistConfig = {
  key: 'root',
  storage: AsyncStorage,
  white: ['errors'],
};

const persistedReducer = persistReducer(persistConfig, rootReducer);

const store = configureStore({
  reducer: persistedReducer,
  middleware: middlewares,
  devTools: process.env.NODE_ENV === `development` ? true : false,
});

// Payment.js(or any of your component)

const handleBtnClick = async (type, value) => {
    console.log('btn click type', type, route.params, value);
    switch (type) {
      case 'pay':
        try {
          let orderId = 'OD_' + moment().format('YYYYMMDDHHmmss');
          // Here we are dispatching action "INITIATE_TRANSACTION" to redux saga.
          // Step 1.
          const response = await props.initiatePayment({
            orderId,
            orderAmount: 10,
            orderCurrency: 'INR',
          });

          console.log('response', response.data.cftoken);
          }catch (error){
          console.log('error', error)
          }
          break;
}

// Middleware used is redux-saga-thunk, which basically implements functionality of thunk in redux saga. so meta: {thunk: true} waits for the whole execution to finish and then continues.
// Step 2
export const initiatePayment = data => ({
  type: INITIATE_TRANSACTION,
  data,
  meta: {thunk: true},
});

//Step 3
// Here post is just a custom function for api call, onError and onSuccess are the handler functions and safe is the wrapper method which basically implements try catch logic and handles errors for us without repeating much of the code.

// paymentSaga.js
function* initiateTransactionSaga({data, meta}) {
  const response = yield call(post, API.INITIATE_TRANSACTION, data);
  return response; //Step 4....
}

export default function* paymentSaga() {
  yield takeLatest(
    INITIATE_TRANSACTION,
    safe(onError, initiateTransactionSaga, onSuccess), //onSuccess required if you want values to be returned to step 1
  );
}

// axiosApi.js

export async function post(url, data, config = {}) {
  console.log('url data config', url, data, config);
  return axiosApi
    .post(url, {...data}, {...config})
    .then(response => response.data);
}

// Sagahelper.js


/**
 * @param handler --- Error handler function. In our case, onError Function
 * @param saga --- Actual Saga function. in our case Api is called and data is returned
 * @param success --- success redux action dispatch function -- in our case, if we need to pass response coming from api to the actual calling function( something like props.viewingItem(data)), then pass it here, otherwise leave it blank
 * @
 */

export const safe = (
  handler: any = null,
  saga: any,
  success: any = null,
  ...args: any
) =>
  function* (action: any) {
    try {
      console.log('action in safe===', action, success);
      const res1 = yield call(saga, ...args, action);
      // Success wrapper. if you pass onSuccess method, then only this part will be executed. If you do not want the values to be returned from redux-saga to your component function call, then i suggest you skip it.
      if (success) {
        yield call(success, res1, action.type, action.meta);
        return res1; //This line returns value to the component function( to step 1)
      }
    } catch (err) {
      yield call(handler, ...args, err, action.type, action.meta);
    }
  };

export function* onError(err: any, type: any, meta: any) {
  console.log('type onError', type);
  yield put({
    type: type + '_ERROR',
    payload: err,
    error: true,
    meta,
  });
  
  // Do something with the error msg. like show alert, etc...
  return err;
}

export function* onSuccess(response: any, type: any, meta: any) {
  console.log('type onError', response);
  yield put({
    type: type + '_SUCCESS',
    payload: response,
    error: false,
    meta,
  });
  // Do something with the success msg. like show alert, etc...
  return response;
}

您可以在此处找到详细信息。 https://medium.com/@dhavaljavia.p/redux-saga-return-values-from-saga-to-your-component-using-redux-saga-thunk-17ad6e9e81ef