React-Reduxでのエラー処理の正しい方法


11

React-Reduxでのエラー処理のより一般的な方法または正しい方法を理解したいと思います。

電話番号登録コンポーネントがあるとします。

そのコンポーネントは、入力された電話番号が無効である場合にエラーをスローします

そのエラーを処理する最良の方法は何でしょうか?

アイデア1: エラーを受け取り、エラーが渡されるたびにアクションをディスパッチするコンポーネントを作成する

アイデア2: エラーはそのコンポーネントに関連しているため、そのエラーをコンポーネントに渡します(コンポーネントはreduxに接続されていません。つまり、エラーハンドラーコンポーネントはアクションをディスパッチしません)。

質問:誰かがReact-Reduxでの大規模アプリのエラー処理の適切な方法について私を案内してくれませんか?


1
ユーザーが何かを行った後、またはすぐに、電話番号の検証は同期または非同期でどのように行われますか?ユーザーに表示して、何をしたいですか?Reduxはアプリの状態を保存するためのもので、質問とは少し関係がないようです。
RemcoGerlich

回答:


3

あなたの最初のアイデアはどちらも全体像を捉えているとは言えません。アイデア1は単なるコールバックです。コールバックを使用する場合:useCallback。reduxを使用する必要がない場合は、アイデア2が機能し、推奨されます。reduxを使用した方がよい場合もあります。たぶん、入力フィールドにエラーなどがないことを確認することで、フォームの有効性を設定しています。reduxのトピックを扱っているので、そうだとしましょう。

通常、reduxを使用したエラー処理への最良のアプローチは、エラーコンポーネントに渡される状態のエラーフィールドを持つことです。

const ExampleErrorComponent= () => {
  const error = useSelector(selectError);
  if (!error) return null;
  return <div className="error-message">{error}</div>;
}

エラーコンポーネントは、エラーを表示するだけでなく、を使用して副作用を発生させることもできuseEffectます。

エラーの設定/設定解除の方法は、アプリケーションによって異なります。電話番号の例を使用してみましょう。

1.妥当性チェックが純粋な関数の場合、レデューサーで実行できます。

次に、電話番号の変更アクションに応じてエラーフィールドを設定または設定解除します。switchステートメントで構築されたレデューサーでは、次のようになります。

case 'PHONE_NUMBER_CHANGE':
  return {
    ...state,
    phoneNumber: action.phoneNumber,
    error: isValidPhoneNumber(action.phoneNumber) ? undefined : 'Invalid phone number',
  };

2.エラーがバックエンドによって報告された場合、エラーアクションをディスパッチします。

電話番号を処理する前に検証を行うバックエンドに電話番号を送信するとします。データがクライアント側で有効かどうかはわかりません。あなたはただそれについてサーバーの言葉を取る必要があるでしょう。

const handleSubmit = useCallback(
  () => sendPhoneNumber(phoneNumber)
    .then(response => dispatch({
      type: 'PHONE_NUMBER_SUBMISSION_SUCCESS',
      response,
    }))
    .catch(error => dispatch({
      type: 'PHONE_NUMBER_SUBMISSION_FAILURE',
      error,
    })),
  [dispatch, phoneNumber],
);

次に、レデューサーはエラーに適切なメッセージを表示し、それを設定する必要があります。

エラーを解除することを忘れないでください。アプリケーションに応じて、変更アクション時または別の要求を行うときにエラーの設定を解除できます。

私が概説した2つのアプローチは相互に排他的ではありません。1番目を使用してローカルで検出可能なエラーを表示し、2番目を使用してサーバー側またはネットワークエラーを表示することもできます。


私はあなたの応答にとても感謝しています。これは間違いなく私がすることに比べてより良いアプローチに見えます。私はまだいくつかの提案を探しているので、この質問について賞金を獲得しました。
anny123

1

私はyup検証でformikを使用します。次に、サーバー側エラーの場合、次のようなものを使用します。

import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Spinner } from "@blueprintjs/core";

export default ({ action, selector, component, errorComponent }) => {
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(action());
  }, [dispatch, action]);

  const DispatchFetch = () => {
    const { data, isRequesting, error } = useSelector(selector());
    if (!isRequesting && data) {
      const Comp = component;
      return <Comp data={data}></Comp>;
    } else if (error) {
      if (errorComponent) {
        const ErrorComp = errorComponent;
        return <ErrorComp error={error}></ErrorComp>;
      }
      return <div>{error}</div>;
    }
    return <Spinner></Spinner>;
  };

  return <DispatchFetch></DispatchFetch>;
};

面白いanercoに見えます、答えてくれてありがとう:)
anny123

1

それはあなたが話しているエラー処理の種類に依存します。フォーム検証処理のみの場合は、Reduxは必要ないと思います。この記事をお読みください。エラーがそのコンポーネント内でのみ「消費」される場合は、なぜそれをreduxに送信するのですか?あなたはそのためにあなたの地方の州を簡単に使うことができます。

一方、サイトでのHTTP呼び出しが失敗したかどうかを示す何らかのエラー通知をユーザーに表示する場合は、アプリケーション(または一般的にはミドルウェア)のすべての部分からエラーをディスパッチすることで、reuxを利用できます。

dispatch({ type: 'SET_ERROR_MESSAGE', error: yourErrorOrMessage });

// simple error message reducer
function errorMessage(state = null, action) {
  const { type, error } = action;

  switch (type) {
      case 'RESET_ERROR_MESSAGE':
          return null;
      case 'SET_ERROR_MESSAGE':
          return error;
  }

  return state
}

状態をどのように編成するのか、ある状態をreduxにするか、コンポーネントのローカル状態に保つ必要があるかを定義する必要があります。すべてをreduxに入れることもできますが、個人的にはやりすぎだと思います。コンポーネントYだけがその状態を気にしているのに、なぜ状態XをコンポーネントYに入れるのでしょうか。コードを正しく構成すれば、なんらかの理由でアプリの他の部分がその状態に依存し始めた場合でも、その状態をローカルからreduxに移動しても問題はありません。


1

このように考えると、どうあるべきか?そして、国家から何を導き出すべきですか?状態はreduxに保存し、派生物を計算する必要があります。

電話番号は状態で、どのフィールドにフォーカスがあるかが状態ですが、それが有効かどうかは、状態の値から導出できます。

Reselectを使用して派生をキャッシュし、関連する状態が変更されていない場合と同じ結果を返します。

export const showInvalidPhoneNumberMessage = createSelector(
  getPhoneValue,
  getFocusedField,
  (val, focus) => focus !== 'phone' && val.length < 10 // add other validations.
)

次に、この値を考慮するすべてのコンポーネントでmapStateToPropsのセレクターを使用できます。また、非同期アクションでも使用できます。フォーカスが変更されていない場合、またはフィールドの値が変更されていない場合、再計算は行われず、代わりに以前の値が返されます。

選択された状態チェックを追加して、複数の状態の断片が1つの派生にどのように結びつくかを示します。

私は個人的に、自分の状態を可能な限り小さく保つことで物事に取り組みます。たとえば、独自のカレンダーを作成したいとします。毎日状態で保存しますか、それとも、現在表示されている現在の年と月など、いくつかのことを知る必要がありますか。これら2つの状態だけで、カレンダーに表示する日数を計算でき、そのうちの1つが変更されるまで再計算する必要はありません。この再計算は実質的に自動的に行われ、可能な方法をすべて考える必要はありません。変化する。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.