web-dev-qa-db-ja.com

React Reduxでフェッチエラーに対処する最良の方法は何ですか?

私はクライアント用に1つのレデューサー、AppToolbar用と他のいくつかのレデューサーを持っています...

ここで、クライアントを削除するフェッチアクションを作成し、それが失敗した場合、クライアントレデューサーに何らかの処理を行うコードがありますが、AppToolbarでグローバルエラーを表示したいとします。

しかし、クライアントとAppToolbarレデューサーは状態の同じ部分を共有しないため、レデューサーで新しいアクションを作成できません。

それでは、グローバルエラーを表示するにはどうすればよいですか?ありがとう

更新1:

este devstackを使用していることを忘れてしまいました

更新2:私はエリックの答えを正しいとマークしましたが、私はesteで使用しているソリューションはエリックとダンの答えの組み合わせに似ていると言わなければなりません...コードに最適なものを見つける必要があります...

84
Dusan Plavak

「グローバルエラー」の概念が必要な場合は、errorsレデューサーを作成して、addError、removeErrorなどのアクションをリッスンできます。その後、state.errorsでRedux状態ツリーにフックし、適切な場所に表示できます。

これにアプローチする方法はいくつかありますが、一般的な考え方は、グローバルエラー/メッセージは、独自のリデューサーに<Clients />/<AppToolbar />とは完全に独立して動作するメリットがあるということです。もちろん、これらのコンポーネントのいずれかがerrorsへのアクセスを必要とする場合、必要に応じて__properとしてerrorsをそれらに渡すことができます。

更新:コード例

「グローバルエラー」errorsをトップレベルの<App />に渡して条件付きでレンダリングした場合(エラーが存在する場合)の例を次に示します。 react-reduxのconnect を使用して、<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);

そして、アクションの作成者に関する限り、応答に従って( 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});
      });
  };
}

レデューサーは単純にエラーの配列を管理できますが、エントリを適切に追加/削除します。

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;
  }
}
98
Erik Aybar

エリックの答え は正しいですが、エラーを追加するために個別のアクションを起動する必要はないことを付け加えます。別のアプローチは、errorフィールドを持つアクションを処理するレデューサーを使用することです。これは個人的な選択と慣習の問題です。

たとえば、 Redux real-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
}
101
Dan Abramov

現在、いくつかの特定のエラー(ユーザー入力検証)に対して取っているアプローチは、サブリデューサーに例外をスローさせ、それをルートレデューサーでキャッチし、アクションオブジェクトにアタッチすることです。次に、アクションオブジェクトのエラーを検査し、その場合のエラーデータで状態ツリーを更新するredux-sagaがあります。

そう:

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

そして、エラーを状態ツリーに追加することは、エリックが説明するとおりです。

私はそれをかなり控えめに使用しますが、それはレデューサーに正当に属するロジックを複製する必要がないようにします(したがって、無効な状態から自分自身を保護することができます)。

1
Gavin

私がやることは、エフェクトごとにすべてのエラー処理をエフェクトに集中させることです

/**
 * 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}`));
    });
0
meanstack

すべてのAPI関連エラーを処理するカスタムミドルウェアを記述します。この場合、コードはよりクリーンになります。

   failure/ error actin type ACTION_ERROR

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

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

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

       }
}
0
Khalid Azam