Manière correcte de gestion des erreurs dans React-Redux

11

Je veux comprendre quelle est la manière la plus générale ou correcte de gérer les erreurs avec React-Redux.

Supposons que j'ai un composant d'inscription au numéro de téléphone.

Ce composant génère une erreur indiquant si le numéro de téléphone saisi n'est pas valide

Quelle serait la meilleure façon de gérer cette erreur?

Idée 1: créer un composant, qui prend une erreur et envoie une action chaque fois qu'une erreur lui est transmise

idée 2: Étant donné que l'erreur est liée à ce composant, transmettez cette erreur à un composant (qui n'est pas connecté à redux, c'est-à-dire que le composant gestionnaire d'erreur ne distribuera pas l'action)

Question: Quelqu'un peut-il me guider sur la manière correcte de gérer les erreurs dans React-Redux pour une application à grande échelle?

anny123
la source
1
Comment se fait la validation du numéro de téléphone, synchrone ou asynchrone, après que l'utilisateur a fait quelque chose ou immédiatement? Que voulez-vous qu'il se passe, visible pour l'utilisateur? Redux sert à stocker l'état de votre application, cela semble un peu sans rapport avec votre question.
RemcoGerlich

Réponses:

3

Je dirais que ni l'une ni l'autre de vos idées initiales ne captent l'ensemble du tableau. L'idée 1 n'est qu'un rappel. Si vous souhaitez utiliser un rappel: useCallback. L'idée 2 fonctionnera et est préférable si vous n'avez pas besoin d'utiliser redux. Parfois, il vaut mieux utiliser redux. Vous définissez peut-être la validité du formulaire en vérifiant qu'aucun des champs de saisie ne contient d'erreurs ou quelque chose de similaire. Puisque nous parlons de redux, supposons que c'est le cas.

Habituellement, la meilleure approche de la gestion des erreurs avec redux consiste à avoir un champ d'erreur dans l'état qui est ensuite transmis à un composant d'erreur.

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

Le composant d'erreur ne doit pas simplement afficher une erreur, il peut également provoquer des effets secondaires useEffect.

La façon dont l'erreur est définie / supprimée dépend de votre application. Prenons l'exemple de votre numéro de téléphone.

1. Si le contrôle de validité est une fonction pure, il peut être effectué dans le réducteur.

Vous devez ensuite définir ou désactiver le champ d'erreur en réponse aux actions de changement de numéro de téléphone. Dans un réducteur construit avec une instruction switch, cela pourrait ressembler à ceci.

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

2. Si des erreurs sont signalées par le backend, envoyez des actions d'erreur.

Supposons que vous envoyez le numéro de téléphone à un serveur principal qui effectue la validation avant de faire quelque chose avec le numéro. Vous ne pouvez pas savoir si les données sont valides côté client. Vous aurez juste à croire le mot du serveur.

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],
);

Le réducteur devrait alors trouver un message approprié pour l'erreur et le régler.

N'oubliez pas de supprimer l'erreur. Vous pouvez annuler l'erreur lors d'une action de modification ou lors d'une autre demande en fonction de l'application.

Les deux approches que j'ai décrites ne s'excluent pas mutuellement. Vous pouvez utiliser le premier pour afficher les erreurs détectables localement et également utiliser le second pour afficher les erreurs côté serveur ou réseau.

Jemi Salo
la source
J'apprécie tellement votre réponse et cela semble définitivement une meilleure approche par rapport à ce que je fais. Je suis toujours à la recherche d'autres suggestions et j'ai donc commencé une prime sur cette question.
anny123
1

J'utiliserais un formik avec validation yup. puis, pour une erreur côté serveur, j'utiliserais quelque chose comme ceci:

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
la source
Semble anerco intéressant, merci d'avoir répondu :)
anny123
1

Cela dépend du type de gestion des erreurs dont vous parlez. Si ce n'est que la gestion de la validation des formulaires, je ne pense pas que vous ayez besoin de Redux pour cela - veuillez lire cet article . Si votre erreur ne sera "consommée" que dans ce composant, pourquoi l'envoyer à redux? Vous pouvez facilement utiliser votre état local pour cela.

D'un autre côté, si vous souhaitez afficher une sorte de notification d'erreur à l'utilisateur indiquant si un appel HTTP sur le site a échoué, vous pouvez bénéficier de redux en envoyant des erreurs de toutes les parties de votre application (ou même génériquement votre middleware)

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
}

Vous devez définir comment votre état va être organisé et si vous devez mettre un état en redux ou simplement le conserver dans l'état local de votre composant. Vous pouvez tout mettre en redux, mais personnellement, je dirais que c'est une exagération - pourquoi mettriez-vous l'état X dans le composant Y si seul le composant Y se soucie de cet état? Si vous structurez correctement votre code, vous ne devriez pas avoir de problème à déplacer cet état de local vers redux plus tard, si pour une raison quelconque d'autres parties de votre application commencent à dépendre de cet état.

zhuber
la source
1

J'y pense comme ça, quel devrait être l'état? Et que devrait-on déduire de l'État? L'état doit être stocké dans redux et les dérivations doivent être calculées.

Un numéro de téléphone est l'état, le champ dont le focus est l'état, mais s'il est valide ou non, cela peut être dérivé des valeurs dans l'état.

J'utiliserais Reselect pour mettre en cache les dérivations et retourner les mêmes résultats lorsque l'état pertinent n'a pas été modifié.

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

Vous pouvez ensuite utiliser le sélecteur dans mapStateToProps dans tous les composants qui peuvent se soucier de cette valeur, et vous pouvez également l'utiliser dans des actions asynchrones. Si le focus n'a pas changé ou si la valeur du champ n'a pas changé, alors aucun recalcul ne se produira, il renverra la valeur précédente à la place.

J'ajoute la vérification d'état sélectionnée juste pour montrer comment plusieurs éléments d'état peuvent se réunir pour entraîner une dérivation.

J'essaie personnellement d'approcher les choses en gardant mon état le plus petit possible. Par exemple, supposons que vous vouliez créer votre propre calendrier. Allez-vous stocker chaque jour dans l'état, ou avez-vous juste besoin de connaître quelques choses comme l'année et le mois en cours actuellement consultés. Avec seulement ces 2 éléments d'état, vous pouvez calculer les jours à afficher sur un calendrier, et n'avez pas besoin de recalculer jusqu'à ce que l'un d'eux change, et ce recalcul se fera pratiquement automatiquement, pas besoin de penser à toutes les façons dont ils pourraient changement.

vert riche
la source