Prawidłowy sposób obsługi błędów w React-Redux

11

Chcę zrozumieć, jaki jest bardziej ogólny lub poprawny sposób obsługi błędów w React-Redux.

Załóżmy, że mam komponent do rejestracji numeru telefonu.

Ten składnik zgłasza błąd, jeśli wprowadzony numer telefonu jest nieprawidłowy

Jaki byłby najlepszy sposób na poradzenie sobie z tym błędem?

Pomysł 1: Utwórz komponent, który pobiera błąd i wywołuje akcję za każdym razem, gdy zostanie do niego przekazany błąd

pomysł 2: Ponieważ błąd jest związany z tym komponentem, przekaż ten błąd do komponentu (który nie jest podłączony do redux, tzn. komponent obsługi błędów nie wywoła akcji)

Pytanie: Czy ktoś może poprowadzić mnie przez proces prawidłowej obsługi błędów w React-Redux dla aplikacji na dużą skalę?

anny123
źródło
1
Jak odbywa się sprawdzanie poprawności numeru telefonu, synchroniczne lub asynchroniczne, gdy użytkownik coś zrobi lub natychmiast? Co chcesz się wydarzyć, widoczne dla użytkownika? Redux służy do przechowywania stanu twojej aplikacji, wydaje się to trochę niezwiązane z twoim pytaniem.
RemcoGerlich,

Odpowiedzi:

3

Powiedziałbym, że żaden z twoich początkowych pomysłów nie oddaje całego obrazu. Pomysł 1 to tylko oddzwonienie. Jeśli chcesz użyć wywołania zwrotnego: useCallback. Pomysł 2 będzie działał i jest lepszy, jeśli nie musisz używać redux. Czasami lepiej jest użyć redux. Może ustawiasz ważność formularza, sprawdzając, czy żadne z pól wejściowych nie zawiera błędów lub czegoś podobnego. Ponieważ zajmujemy się redux, załóżmy, że tak jest.

Zwykle najlepszym podejściem do obsługi błędów z redux jest posiadanie pola błędu w stanie, które jest następnie przekazywane do składnika błędu.

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

Składnik błędu nie musi tylko wyświetlać błędu, może również powodować działania niepożądane useEffect.

Sposób ustawienia / wyłączenia błędu zależy od aplikacji. Użyjmy twojego numeru telefonu.

1. Jeśli kontrola ważności jest funkcją czystą, można to zrobić w reduktorze.

Następnie należy ustawić lub rozbroić pole błędu w odpowiedzi na działania zmiany numeru telefonu. W reduktorze zbudowanym z instrukcji switch może to wyglądać tak.

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

2. Jeśli backend zgłasza błędy, wyślij akcje błędów.

Powiedzmy, że wysyłasz numer telefonu do wewnętrznej bazy danych, która sprawdza, zanim coś zrobi z tym numerem. Nie możesz wiedzieć, czy dane są prawidłowe po stronie klienta. Musisz po prostu uwierzyć na słowo serwera.

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

Reduktor powinien następnie wyświetlić odpowiedni komunikat o błędzie i ustawić go.

Nie zapomnij usunąć błędu. Możesz usunąć błąd podczas akcji zmiany lub przy kolejnym żądaniu, w zależności od aplikacji.

Dwa przedstawione przeze mnie podejścia nie wykluczają się wzajemnie. Możesz użyć pierwszego, aby wyświetlić wykrywalne lokalnie błędy, a także drugiego, aby wyświetlić błędy po stronie serwera lub sieci.

Jemi Salo
źródło
Bardzo doceniam twoją odpowiedź i to zdecydowanie wygląda na lepsze podejście niż to, co robię. Nadal szukam kilku sugestii i dlatego zacząłem nagrodę za to pytanie.
anny123
1

Chciałbym użyć formika z weryfikacją yup. następnie dla błędu po stronie serwera użyłbym czegoś takiego:

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
źródło
Wygląda interesująco anerco, dziękuję za odpowiedź :)
anny123 15.10.19
1

To zależy od rodzaju obsługi błędów, o której mówisz. Jeśli dotyczy to tylko sprawdzania poprawności formularzy, nie sądzę, że potrzebujesz do tego Redux - przeczytaj ten artykuł . Jeśli twój błąd zostanie „wykorzystany” tylko w tym komponencie, po co wysyłać go do redux? Możesz do tego łatwo użyć swojego lokalnego stanu.

Z drugiej strony, jeśli chcesz pokazać użytkownikowi jakieś powiadomienie o błędzie wskazujące, czy jakiekolwiek połączenie HTTP w witrynie nie powiodło się, możesz skorzystać z reduxu, wysyłając błąd ze wszystkich części aplikacji (lub nawet ogólnie oprogramowania pośredniego)

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
}

Musisz zdefiniować, w jaki sposób twój stan będzie zorganizowany i czy chcesz wprowadzić jakiś stan w redux, czy po prostu utrzymać go w lokalnym stanie swojego komponentu. Mógłbyś wszystko odmienić, ale osobiście powiedziałbym, że to przesada - dlaczego miałbyś umieszczać stan X w składniku Y, skoro tylko składnik Y dba o ten stan? Jeśli poprawnie skonstruujesz swój kod, nie powinieneś mieć problemu z przeniesieniem tego stanu z lokalnego na redux później, jeśli z jakiegoś powodu inne części aplikacji zaczną zależeć od tego stanu.

zhuber
źródło
1

Myślę o tym w ten sposób, co powinno być stanem? A co powinno pochodzić od państwa? Stan powinien być przechowywany jako redux, a pochodne powinny być obliczane.

Numer telefonu to stan, którego pole koncentruje się na stanie, ale bez względu na to, czy jest poprawny, można go wyprowadzić z wartości w stanie.

Używałbym Reselect do buforowania pochodnych i zwracał te same wyniki, gdy odpowiedni stan nie został zmodyfikowany.

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

Następnie możesz użyć selektora w mapStateToProps we wszystkich komponentach, które mogą mieć znaczenie dla tej wartości, a także możesz użyć jej w akcjach asynchronicznych. Jeśli fokus nie zmienił się lub wartość pola nie uległa zmianie, wówczas nie nastąpi ponowne obliczenie, zamiast tego zostanie zwrócona poprzednia wartość.

Dodam zaznaczone sprawdzenie stanu tylko po to, aby pokazać, jak wiele kawałków stanu może zebrać się w jeden wynik.

Osobiście staram się podchodzić do rzeczy, utrzymując mój stan na jak najniższym poziomie. Załóżmy na przykład, że chcesz utworzyć własny kalendarz. Czy będziesz przechowywać każdego dnia w stanie, czy po prostu musisz wiedzieć kilka rzeczy, takich jak bieżący rok i miesiąc, które są obecnie przeglądane. Dzięki tym dwóm stanom możesz obliczyć liczbę dni do wyświetlenia w kalendarzu i nie trzeba ponownie obliczać, dopóki jedno z nich się nie zmieni, a to ponowne obliczenie nastąpi praktycznie automatycznie, bez potrzeby zastanawiania się nad wszystkimi możliwymi sposobami zmiana.

bogata zieleń
źródło