【问题标题】:React with Redux Saga - How to get data back from axios callReact with Redux Saga - 如何从 axios 调用中取回数据
【发布时间】:2021-03-27 14:09:26
【问题描述】:

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

dispatch to route => 发回 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));

如果我控制台记录我刚刚返回的资源,我刚刚发送了 Obj。

问题:

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

即res 给了我刚刚插入的 obj

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

我再说一遍,从 dispatch 到 reducer 的每一件事都完美无缺(我省略了)我只是想看看我是否可以扩展我的 SAGA 知识

【问题讨论】:

  • 我知道我以前回答过这个问题。使用redux-thunk 很容易做到这一点,但使用redux-saga 返回的值只是您调度的 first 操作。因此,在您的 saga 中触发的其他操作是独立的。

标签: reactjs redux react-redux redux-saga


【解决方案1】:

与佐贺

调用dispatch 的返回值就是你派发的动作。您的 saga 将“采取”此操作并调度其他操作,但这都被认为是独立的。因此,您的 res 变量只是原始操作 postSpotifyAlbums(obj)

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

是的,您需要在侦听已发布值的组件中包含 useSelector


使用 Thunk

使用 更容易做到这一点,因为 thunk 中间件会覆盖 dispatch 的默认行为。当您分派一个 thunk(一个动作创建者,它是 dispatch 的函数)时,您会取回 thunk 返回的值。

内部函数的任何返回值都可以作为 dispatch 本身的返回值。这便于编排异步控制流,thunk action creators 相互调度并返回 Promises 以等待彼此完成 - 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

包含一个辅助函数createAsyncThunk,它可以一步自动创建“待处理”、“拒绝”和“已完成”操作。这里的返回值是被分派的最终动作。

当分派时,thunk 将...返回一个包含最终分派操作(fulfilledrejected 操作对象)的已履行承诺 - 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;
  }
};

【讨论】:

  • 这是一个很好的答案。我从未尝试过佐贺。我会试试看。
  • @AjeetShah saga 非常强大,但也让人头疼。对于大多数应用程序来说,这可能是矫枉过正。
  • 非常感谢您的澄清,我保存了您的 Thunk 回复以供下次使用,因为我不知道该工具。是的,Saga 对于大多数用途和这个用途来说绝对是矫枉过正。这个项目是一个大沙盒,所以我决定尝试使用 Saga,是的,这太痛苦了。
【解决方案2】:

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

  1. 我们从我们的组件发出调度请求
  2. redux 调度名为“INITIATE_TRANSACTION”的操作。
  3. redux saga 拦截了 action 并开始基于它进行 api 调用
  4. API 调用成功,“INITIATE_TRANSACTION_SUCCESS”与 api 的值一起分派。
  5. 现在 Saga 将值从 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

【讨论】:

    猜你喜欢
    • 2019-02-21
    • 1970-01-01
    • 2017-05-14
    • 2021-09-20
    • 2020-12-24
    • 2018-11-30
    • 1970-01-01
    • 2018-09-03
    • 2022-01-15
    相关资源
    最近更新 更多