Modo corretto di gestione degli errori in React-Redux


11

Voglio capire qual è il modo più generale o corretto di gestire gli errori con React-Redux.

Supponiamo che io abbia un componente di registrazione del numero di telefono.

Quel componente genera un errore dire se il numero di telefono di input non è valido

Quale sarebbe il modo migliore per gestire questo errore?

Idea 1: creare un componente, che accetta un errore e invia un'azione ogni volta che gli viene passato un errore

idea 2: poiché l'errore è correlato a quel componente, passa l'errore a un componente (che non è collegato a Redux, ovvero il componente del gestore degli errori non invierà l'azione)

Domanda: Qualcuno può guidarmi sulla corretta gestione degli errori in React-Redux per app su larga scala?


1
Come avviene la convalida del numero di telefono, sincrono o asincrono, dopo che l'utente ha fatto qualcosa o immediatamente? Che cosa vuoi che accada, visibile all'utente? Redux serve per memorizzare lo stato della tua app, sembra un po 'estraneo alla tua domanda.
RemcoGerlich,

Risposte:


3

Direi che nessuna delle tue idee iniziali cattura l'intero quadro. Idea 1 è solo una richiamata. Se si desidera utilizzare un callback: useCallback. Idea 2 funzionerà ed è preferibile se non è necessario utilizzare redux. A volte stai meglio usando redux. Forse stai impostando la validità del modulo controllando che nessuno dei campi di input contenga errori o qualcosa di simile. Dato che siamo in tema di redux, supponiamo che sia così.

Di solito, l'approccio migliore alla gestione degli errori con Redux consiste nell'avere un campo di errore nello stato che viene quindi passato a un componente di errore.

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

Il componente errore non deve solo visualizzare un errore, ma potrebbe anche causare effetti collaterali useEffect.

La modalità di impostazione / disinserimento dell'errore dipende dall'applicazione. Usiamo l'esempio del tuo numero di telefono.

1. Se il controllo di validità è una funzione pura, può essere eseguito nel riduttore.

Quindi impostare o annullare l'impostazione del campo di errore in risposta alle azioni di modifica del numero di telefono. In un riduttore costruito con un'istruzione switch potrebbe apparire così.

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

2. Se il back-end segnala errori, inviare le azioni di errore.

Supponiamo che tu stia inviando il numero di telefono a un back-end che esegue la convalida prima di fare qualcosa con il numero. Non puoi sapere se i dati sono validi sul lato client. Devi solo prendere la parola del server per questo.

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

Il riduttore dovrebbe quindi presentare un messaggio appropriato per l'errore e impostarlo.

Non dimenticare di annullare l'errore. È possibile annullare l'errore su un'azione di modifica o quando si effettua un'altra richiesta a seconda dell'applicazione.

I due approcci che ho delineato non si escludono a vicenda. È possibile utilizzare il primo per visualizzare errori rilevabili localmente e anche il secondo per visualizzare errori lato server o di rete.


Apprezzo molto la tua risposta e questo sembra decisamente un approccio migliore rispetto a quello che faccio. Sto ancora cercando altri suggerimenti e quindi ho iniziato una generosità su questa domanda.
anny123,

1

Vorrei usare un formik con validazione yup. quindi, per errore lato server, utilizzerei qualcosa del genere:

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

Sembra interessante anerco, grazie per aver risposto :)
anny123

1

Dipende dal tipo di gestione degli errori di cui stai parlando. Se si tratta solo di gestire la convalida dei moduli, non penso che tu abbia bisogno di Redux per questo - leggi questo articolo . Se il tuo errore verrà "consumato" solo all'interno di quel componente, perché inviarlo a Redux? Puoi facilmente usare il tuo stato locale per questo.

D'altra parte, se si desidera mostrare all'utente un tipo di notifica di errore che indica se una chiamata HTTP sul sito non è riuscita, è possibile trarre vantaggio dal redux inviando l'errore da tutte le parti dell'applicazione (o anche genericamente il 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
}

Devi definire come sarà organizzato il tuo stato e se devi mettere un certo stato in redux o semplicemente mantenerlo nello stato locale del tuo componente. Potresti mettere tutto in ordine, ma personalmente direi che è eccessivo - perché dovresti mettere lo stato X nel componente Y se solo il componente Y si preoccupasse di quello stato? Se strutturi correttamente il codice, non dovresti avere problemi a spostare lo stato da locale a redux in un secondo momento, se per qualche motivo altre parti della tua app iniziano a dipendere da quello stato.


1

Ci penso in questo modo, quale dovrebbe essere lo stato? E cosa dovrebbe essere derivato dallo stato? Lo stato deve essere archiviato in redux e le derivazioni devono essere calcolate.

Un numero di telefono è stato, il cui campo è attivo è stato, ma se è valido o meno, può essere derivato dai valori in stato.

Vorrei utilizzare Reselect per memorizzare nella cache le derivazioni e restituire gli stessi risultati quando lo stato rilevante non è stato modificato.

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

È quindi possibile utilizzare il selettore in mapStateToProps in tutti i componenti che potrebbero interessare questo valore e utilizzarlo anche in azioni asincrone. Se lo stato attivo non è cambiato o il valore del campo non è cambiato, allora non si verificherà alcun ricalcolo, restituirà invece il valore precedente.

Sto aggiungendo il controllo di stato selezionato solo per mostrare come più parti di stato possono unirsi per dare origine a una derivazione.

Personalmente cerco di avvicinarmi alle cose mantenendo il mio stato il più piccolo possibile. Ad esempio, supponiamo che tu voglia creare il tuo calendario. Conserverai ogni singolo giorno nello stato o hai solo bisogno di sapere un paio di cose come l'anno corrente e il mese attualmente visualizzato. Con solo questi 2 stati puoi calcolare i giorni da visualizzare su un calendario e non è necessario ricalcolare fino a quando uno di essi non cambia, e questo ricalcolo avverrà praticamente automaticamente, non è necessario pensare a tutti i modi in cui potrebbero modificare.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.