Forma correcta de manejo de errores en React-Redux

11

Quiero entender cuál es la forma más general o correcta de manejo de errores con React-Redux.

Supongamos que tengo un componente de registro de número de teléfono.

Ese componente arroja un error que dice si el número de teléfono de entrada no es válido

¿Cuál sería la mejor manera de manejar ese error?

Idea 1: Crear un componente, que toma un error y envía una acción cada vez que se le pasa un error

idea 2: Dado que el error está relacionado con ese componente, pase ese error a un componente (que no está conectado a redux, es decir, el componente controlador de errores no enviará la acción)

Pregunta: ¿Alguien puede guiarme sobre la forma correcta de manejo de errores en React-Redux para aplicaciones a gran escala?

anny123
fuente
1
¿Cómo se realiza la validación del número de teléfono, síncrono o asíncrono, después de que el usuario hace algo o de inmediato? ¿Qué quieres que suceda, visible para el usuario? Redux es para almacenar el estado de su aplicación, parece un poco ajeno a su pregunta.
RemcoGerlich

Respuestas:

3

Yo diría que ninguna de sus ideas iniciales captura la imagen completa. La idea 1 es solo una devolución de llamada. Si desea utilizar una devolución de llamada: useCallback. Idea 2 funcionará y es preferible si no necesita usar redux. A veces es mejor usar redux. Quizás esté configurando la validez del formulario marcando que ninguno de los campos de entrada tenga errores o algo similar. Como estamos en el tema de redux, supongamos que ese es el caso.

Por lo general, el mejor enfoque para el manejo de errores con redux es tener un campo de error en estado que luego se pasa a un componente de error.

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

El componente de error no solo tiene que mostrar un error, también podría causar efectos secundarios useEffect.

La forma en que se establece / desarma el error depende de su aplicación. Usemos su ejemplo de número de teléfono.

1. Si la verificación de validez es una función pura, se puede hacer en el reductor.

Luego establecería o desarmaría el campo de error en respuesta a las acciones de cambio de número de teléfono. En un reductor construido con una declaración de cambio, podría verse así.

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

2. Si el backend informa de errores, envíe acciones de error.

Digamos que está enviando el número de teléfono a un backend que valida antes de hacer algo con el número. No puede saber si los datos son válidos en el lado del cliente. Solo tendrá que tomar la palabra del servidor.

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

El reductor debería aparecer con un mensaje apropiado para el error y configurarlo.

No olvides desarmar el error. Puede desactivar el error en una acción de cambio o al realizar otra solicitud según la aplicación.

Los dos enfoques que describí no son mutuamente excluyentes. Puede usar el primero para mostrar errores detectables localmente y también usar el segundo para mostrar errores del lado del servidor o de la red.

Jemi Salo
fuente
Aprecio mucho su respuesta y este definitivamente parece un mejor enfoque en comparación con lo que hago. Todavía estoy buscando algunas sugerencias más y, por lo tanto, he comenzado una recompensa por esta pregunta.
anny123
1

Usaría un formik con validación yup. entonces, para el error del lado del servidor, usaría algo como esto:

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
fuente
Parece interesante anerco, gracias por responder :)
anny123
1

Depende de qué tipo de manejo de errores estás hablando. Si solo se trata de la validación de formularios, no creo que necesite Redux para eso, lea este artículo . Si su error solo se va a "consumir" dentro de ese componente, ¿por qué enviarlo a redux? Puede usar fácilmente su estado local para eso.

Por otro lado, si desea mostrar algún tipo de notificación de error al usuario que indique si alguna llamada HTTP en el sitio falló, podría beneficiarse con redux enviando un error desde todas las partes de su aplicación (o incluso genéricamente su 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
}

Debe definir cómo se organizará su estado y si necesita poner algún estado en redux o simplemente mantenerlo en el estado local de su componente. Podrías poner todo en redux, pero personalmente diría que es una exageración: ¿por qué pondrías el estado X en el componente Y si solo al componente Y le importa ese estado? Si estructura su código correctamente, no debería tener problemas para mover ese estado de local a redux más adelante, si por alguna razón otras partes de su aplicación comienzan a depender de ese estado.

zhuber
fuente
1

Lo pienso así, ¿cuál debería ser el estado? ¿Y qué se debe derivar del estado? El estado debe almacenarse en redux, y las derivaciones deben calcularse.

Un número de teléfono es estado, cuyo campo tiene foco es estado, pero si es válido o no, eso puede derivarse de los valores en estado.

Usaría Reselect para derivaciones de caché y devolvería los mismos resultados cuando el estado relevante no se haya modificado.

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

Luego puede usar el selector en mapStateToProps en todos los componentes que puedan interesarse por este valor, y también puede usarlo en acciones asíncronas. Si el foco no ha cambiado, o el valor del campo no ha cambiado, entonces no se volverá a calcular, en su lugar devolverá el valor anterior.

Estoy agregando la verificación de estado seleccionada solo para mostrar cómo se pueden unir varias piezas de estado para dar como resultado una derivación.

Personalmente trato de abordar las cosas manteniendo mi estado lo más pequeño posible. Por ejemplo, supongamos que desea crear su propio calendario. ¿Almacenará todos los días en el estado o solo necesita saber un par de cosas como el año y el mes actual que se está viendo actualmente? Con solo estos 2 elementos de estado, puede calcular los días que se mostrarán en un calendario, y no necesita volver a calcular hasta que uno de ellos cambie, y este recálculo sucederá de forma prácticamente automática, no es necesario pensar en todas las formas en que podrían cambio.

verde rico
fuente