Maneira correta de tratamento de erros no React-Redux

11

Quero entender qual é a maneira mais geral ou correta de lidar com erros com o React-Redux.

Suponha, eu tenho o componente de inscrição de número de telefone.

Esse componente gera um erro, digamos se o número de telefone de entrada é inválido

Qual seria a melhor maneira de lidar com esse erro?

Ideia 1: Criar um componente, que recebe um erro e envia uma ação sempre que um erro é passado para ele

idéia 2: como o erro está relacionado a esse componente, passe esse erro para um componente (que não está conectado ao redux, ou seja, o componente manipulador de erros não despacha a ação)

Pergunta: Alguém pode me orientar sobre a maneira correta de lidar com erros no React-Redux para aplicativos em larga escala?

anny123
fonte
11
Como acontece a validação do número de telefone, síncrona ou assíncrona, depois que o usuário faz alguma coisa ou imediatamente? O que você quer que aconteça, visível para o usuário? O Redux é para armazenar o estado do seu aplicativo, parece um pouco não relacionado à sua pergunta.
RemcoGerlich 7/10/19

Respostas:

3

Eu diria que nenhuma de suas idéias iniciais captura o quadro inteiro. A idéia 1 é apenas um retorno de chamada. Se você quiser usar uma chamada de retorno: useCallback. A idéia 2 funcionará e é preferível se você não precisar usar o redux. Às vezes, é melhor usar o redux. Talvez você esteja configurando a validade do formulário verificando se nenhum dos campos de entrada possui erros ou algo semelhante. Como estamos no tópico redux, vamos assumir que é o caso.

Geralmente, a melhor abordagem para o tratamento de erros com redux é ter um campo de erro no estado que é passado para um componente de erro.

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

O componente de erro não precisa apenas exibir um erro, ele também pode causar efeitos colaterais useEffect.

Como o erro é definido / desabilitado depende do seu aplicativo. Vamos usar o seu exemplo de número de telefone.

1. Se a verificação de validade for uma função pura, isso pode ser feito no redutor.

Você definiria ou desabilitaria o campo de erro em resposta às ações de alteração do número de telefone. Em um redutor construído com uma instrução switch, poderia ser assim.

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

2. Se os erros forem relatados pelo back-end, despache as ações de erro.

Digamos que você esteja enviando o número de telefone para um back-end que faz a validação antes de fazer algo com o número. Você não pode saber se os dados são válidos no lado do cliente. Você apenas terá que aceitar a palavra do 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],
);

O redutor deve então aparecer com uma mensagem apropriada para o erro e configurá-lo.

Não se esqueça de cancelar o erro. Você pode desativar o erro em uma ação de alteração ou ao fazer outra solicitação, dependendo do aplicativo.

As duas abordagens que descrevi não são mutuamente exclusivas. Você pode usar o primeiro para exibir erros detectáveis ​​localmente e também o segundo para exibir erros do servidor ou de rede.

Jemi Salo
fonte
Aprecio muito sua resposta e isso definitivamente parece uma abordagem melhor em comparação com o que faço. Ainda estou procurando mais algumas sugestões e, portanto, comecei uma recompensa nessa questão.
Anny123 09/10/19
1

Eu usaria um formik com validação de yup. então, por erro no servidor, eu usaria algo como isto:

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
fonte
Looks interessante anerco, obrigado por responder :)
anny123
1

Depende de que tipo de tratamento de erros você está falando. Se for apenas manipulação de validação de formulário, acho que você não precisa do Redux para isso - leia este artigo . Se o seu erro será "consumido" apenas dentro desse componente, por que enviá-lo ao redux? Você pode facilmente usar seu estado local para isso.

Por outro lado, se você deseja mostrar algum tipo de notificação de erro ao usuário indicando se alguma chamada HTTP falhou no site, você pode se beneficiar do redux enviando um erro de todas as partes do seu aplicativo (ou mesmo genericamente do seu 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
}

Você precisa definir como o seu estado será organizado e se você deve colocar algum estado no redux ou apenas mantê-lo no estado local do seu componente. Você poderia colocar tudo no redux, mas pessoalmente eu diria que é um exagero - por que você colocaria o estado X no componente Y se apenas o componente Y se importa com esse estado? Se você estruturar seu código corretamente, não deverá ter problemas ao mover esse estado do local para o redux posteriormente, se por algum motivo outras partes do seu aplicativo começarem a depender desse estado.

zhuber
fonte
1

Eu penso assim, como deveria ser o estado? E o que deve ser derivado do estado? O estado deve ser armazenado em redux e as derivações devem ser calculadas.

Um número de telefone é state, cujo campo tem foco é state, mas, válido ou não, pode ser derivado dos valores no state.

Eu usaria Reselect para armazenar em cache derivações e retornar os mesmos resultados quando o estado relevante não tiver sido modificado.

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

Em seguida, você pode usar o seletor em mapStateToProps em todos os componentes que possam se importar com esse valor e também em ações assíncronas. Se o foco não foi alterado ou o valor do campo não foi alterado, nenhum recálculo ocorrerá; ele retornará o valor anterior.

Estou adicionando a verificação do estado selecionado apenas para mostrar como várias partes do estado podem se unir para resultar em uma derivação.

Eu, pessoalmente, tento abordar as coisas mantendo meu estado o menor possível. Por exemplo, digamos que você queira criar seu próprio calendário. Você armazena todos os dias no estado ou precisa apenas conhecer algumas coisas, como o ano e o mês atuais que estão sendo visualizados no momento. Com apenas essas duas partes do estado, você pode calcular os dias a serem exibidos em um calendário e não precisa recalcular até que um deles mude, e esse recálculo ocorrerá praticamente automaticamente, sem a necessidade de pensar em todas as maneiras possíveis. mudança.

verde rico
fonte