Javascript 处理 react redux 中的 fetch 错误的最佳方法是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34403269/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 16:17:37  来源:igfitidea点击:

What is the best way to deal with a fetch error in react redux?

javascriptreactjsredux

提问by Dusan Plavak

I have one reducer for Clients, one other for AppToolbar and some others...

我有一个用于客户端的减速器,另一个用于 AppToolbar 和其他一些...

Now lets say that I created a fetch action to delete client, and if it fails I have code in the Clients reducer which should do some stuff, but also I want to display some global error in AppToolbar.

现在假设我创建了一个 fetch 操作来删除客户端,如果它失败,我在 Clients reducer 中有代码应该做一些事情,但我也想在 AppToolbar 中显示一些全局错误。

But the Clients and the AppToolbar reducers do not share the same part of the state and I cannot create a new action in the reducer.

但是客户端和 AppToolbar 减速器不共享状态的相同部分,我无法在减速器中创建新动作。

So how am I suppose to show global error? Thanks

那么我该如何显示全局错误呢?谢谢

UPDATE 1:

更新1:

I forget to mention that I use estedevstack

我忘了提到我使用estedevstack

UPDATE 2:I marked Eric's answer as correct, but I have to say that solution which I am using in este is more like combination of Eric and Dan's answer... You just have to find what fits you the best in your code...

更新 2:我将 Eric 的答案标记为正确,但我不得不说我在 este 中使用的解决方案更像是 Eric 和 Dan 的答案的组合......你只需要在你的代码中找到最适合你的...... .

回答by Erik Aybar

If you want to have the concept of "global errors", you can create an errorsreducer, which can listen for addError, removeError, etc... actions. Then, you can hook into your Redux state tree at state.errorsand display them wherever appropriate.

如果你想有“全局错误”的概念,你可以创建一个errorsreducer,它可以监听addError、removeError等...动作。然后,您可以挂接到您的 Redux 状态树state.errors并在适当的地方显示它们。

There are a number of ways you could approach this, but the general idea is that global errors/messages would merit their own reducer to live completely separate from <Clients />/<AppToolbar />. Of course if either of these components needs access to errorsyou could pass errorsdown to them as a prop wherever needed.

有多种方法可以解决这个问题,但一般的想法是全局错误/消息值得他们自己的减速器与<Clients />/完全分开<AppToolbar />。当然,如果这些组件中的任何一个需要访问,errors您可以errors在需要时将它们作为道具传递给它们。

Update: Code Example

更新:代码示例

Here is one example of what it might look like if you were to pass the "global errors" errorsinto your top level <App />and conditionally render it (if there are errors present). Using react-redux's connectto hook up your <App />component to some data.

这是一个示例,说明如果您将“全局错误”传递errors到顶层<App />并有条件地呈现它(如果存在错误),它可能会是什么样子。使用react-reduxconnect将您的<App />组件连接到一些数据。

// App.js
// Display "global errors" when they are present
function App({errors}) {
  return (
    <div>
      {errors && 
        <UserErrors errors={errors} />
      }
      <AppToolbar />
      <Clients />
    </div>
  )
}

// Hook up App to be a container (react-redux)
export default connect(
  state => ({
    errors: state.errors,
  })
)(App);

And as far as the action creator is concerned, it would dispatch (redux-thunk) success failure according to the response

就动作创建者而言,它会根据响应调度(redux-thunk)成功失败

export function fetchSomeResources() {
  return dispatch => {
    // Async action is starting...
    dispatch({type: FETCH_RESOURCES});

    someHttpClient.get('/resources')

      // Async action succeeded...
      .then(res => {
        dispatch({type: FETCH_RESOURCES_SUCCESS, data: res.body});
      })

      // Async action failed...
      .catch(err => {
        // Dispatch specific "some resources failed" if needed...
        dispatch({type: FETCH_RESOURCES_FAIL});

        // Dispatch the generic "global errors" action
        // This is what makes its way into state.errors
        dispatch({type: ADD_ERROR, error: err});
      });
  };
}

While your reducer could simply manage an array of errors, adding/removing entries appropriately.

虽然您的减速器可以简单地管理一系列错误,但可以适当地添加/删除条目。

function errors(state = [], action) {
  switch (action.type) {

    case ADD_ERROR:
      return state.concat([action.error]);

    case REMOVE_ERROR:
      return state.filter((error, i) => i !== action.index);

    default:
      return state;
  }
}

回答by Dan Abramov

Erik's answeris correct but I would like to add that you don't have to fire separate actions for adding errors. An alternative approach is to have a reducer that handles any action with an errorfield. This is a matter of personal choice and convention.

Erik 的回答是正确的,但我想补充一点,您不必为添加错误而触发单独的操作。另一种方法是使用一个 reducer 来处理带有error字段的任何操作。这是个人选择和惯例的问题。

For example, from Redux real-worldexamplethat has error handling:

例如,来自具有错误处理功能的Reduxreal-world示例

// Updates error message to notify about the failed fetches.
function errorMessage(state = null, action) {
  const { type, error } = action

  if (type === ActionTypes.RESET_ERROR_MESSAGE) {
    return null
  } else if (error) {
    return error
  }

  return state
}

回答by Gavin

The approach I'm currently taking for a few specific errors (user input validation) is to have my sub-reducers throw an exception, catch it in my root reducer, and attach it to the action object. Then I have a redux-saga that inspects action objects for an error and update the state tree with error data in that case.

我目前针对一些特定错误(用户输入验证)采取的方法是让我的子减速器抛出异常,在我的根减速器中捕获它,并将其附加到操作对象。然后我有一个 redux-saga,它检查动作对象是否有错误,并在这种情况下用错误数据更新状态树。

So:

所以:

function rootReducer(state, action) {
  try {
    // sub-reducer(s)
    state = someOtherReducer(state,action);
  } catch (e) {
    action.error = e;
  }
  return state;
}

// and then in the saga, registered to take every action:
function *errorHandler(action) {
  if (action.error) {
     yield put(errorActionCreator(error));
  }
}

And then adding the error to the state tree is as Erik describes.

然后将错误添加到状态树中,就像 Erik 描述的那样。

I use it pretty sparingly, but it keeps me from having to duplicate logic which legitimately belongs in the reducer (so it can protect itself from an invalid state).

我非常谨慎地使用它,但它使我不必复制合法属于减速器的逻辑(因此它可以保护自己免受无效状态的影响)。

回答by Khalid Azam

write custom Middleware to handle all the api related error. In this case your code will be more cleaner.

编写自定义中间件来处理所有与 api 相关的错误。在这种情况下,您的代码将更加简洁。

   failure/ error actin type ACTION_ERROR

   export default  (state) => (next) => (action) => {

      if(ACTION_ERROR.contains('_ERROR')){

       // fire error action
        store.dispatch(serviceError());

       }
}

回答by meanstack

what I do is I centralize all error handling in the effect on a per effect basis

我所做的是将所有错误处理集中在每个效果的基础上

/**
 * central error handling
 */
@Effect({dispatch: false})
httpErrors$: Observable<any> = this.actions$
    .ofType(
        EHitCountsActions.HitCountsError
    ).map(payload => payload)
    .switchMap(error => {
        return of(confirm(`There was an error accessing the server: ${error}`));
    });

回答by Phú ??

You can use axios HTTP client. It already has implemented Interceptors feature. You can intercept requests or responses before they are handled by then or catch.

您可以使用 axios HTTP 客户端。它已经实现了拦截器功能。您可以在它们被 then 或 catch 处理之前拦截请求或响应。

https://github.com/mzabriskie/axios#interceptors

https://github.com/mzabriskie/axios#interceptors

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });