Правильный способ обработки ошибок в React-Redux

11

Я хочу понять, что является более общим или правильным способом обработки ошибок с помощью React-Redux.

Предположим, у меня есть компонент для регистрации номера телефона.

Этот компонент выдает ошибку, скажем, если номер телефона введен неверно

Как лучше всего справиться с этой ошибкой?

Идея 1: Создать компонент, который принимает ошибку и отправляет действие всякий раз, когда ему передается ошибка

идея 2: так как ошибка связана с этим компонентом, передайте эту ошибку компоненту (который не связан с redux, то есть компонент обработчика ошибок не будет отправлять действие)

Вопрос: Может ли кто-нибудь подсказать мне, как правильно обрабатывать ошибки в React-Redux для крупномасштабного приложения?

anny123
источник
1
Как происходит проверка номера телефона, синхронного или асинхронного, после того, как пользователь что-то делает или немедленно? Что вы хотите, чтобы это было видно пользователю? Redux предназначен для сохранения состояния вашего приложения, кажется, оно не связано с вашим вопросом.
RemcoGerlich

Ответы:

3

Я бы сказал, что ни одна из ваших первоначальных идей не отражает всю картину. Идея 1 это просто обратный звонок. Если вы хотите использовать функцию обратного вызова: useCallback. Идея 2 будет работать и предпочтительнее, если вам не нужно использовать излишек. Иногда вам лучше использовать редукс. Возможно, вы устанавливаете валидность формы, проверяя, что ни в одном из полей ввода нет ошибок или чего-то подобного. Поскольку мы говорим о редуксе, давайте предположим, что это так.

Обычно лучший подход к обработке ошибок с помощью приставки - это иметь поле ошибки в состоянии, которое затем передается компоненту ошибки.

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

Компонент error не должен просто отображать ошибку, он также может вызывать побочные эффекты useEffect.

То, как ошибка установлена ​​/ не установлена, зависит от вашего приложения. Давайте использовать ваш пример номера телефона.

1. Если проверка достоверности является чистой функцией, это можно сделать в редукторе.

Затем вы должны установить или сбросить поле ошибки в ответ на действия по изменению номера телефона. В редукторе, построенном с оператором switch, это может выглядеть следующим образом.

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

2. Если серверная часть сообщает об ошибках, отправьте действия по обработке ошибок.

Допустим, вы отправляете номер телефона бэкэнду, который выполняет проверку перед тем, как что-то сделать с номером. Вы не можете знать, действительны ли данные на стороне клиента. Вам просто нужно поверить слову сервера.

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

Затем редуктор должен создать соответствующее сообщение об ошибке и установить его.

Не забудьте сбросить ошибку. Вы можете сбросить ошибку при изменении действия или при выполнении другого запроса в зависимости от приложения.

Два подхода, которые я изложил, не являются взаимоисключающими. Первое можно использовать для отображения локально обнаруживаемых ошибок, а также второе для отображения ошибок на стороне сервера или сети.

Джеми Сало
источник
Я так ценю ваш ответ, и это определенно выглядит лучше, чем то, что я делаю. Я все еще ищу еще несколько предложений и, следовательно, я начал щедрость по этому вопросу.
anny123
1

Я бы использовал formik с проверкой yup. тогда для ошибки на стороне сервера я бы использовал что-то вроде этого:

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
источник
Выглядит интересно anerco, спасибо, что ответили :)
anny123
1

Это зависит от того, о какой обработке ошибок вы говорите. Если это только обработка проверки формы, тогда я не думаю, что вам нужен Redux для этого - пожалуйста, прочитайте эту статью . Если ваша ошибка будет «поглощена» только этим компонентом, зачем отправлять ее в redux? Вы можете легко использовать свое местное государство для этого.

С другой стороны, если вы хотите показывать пользователю какое-либо уведомление об ошибке, указывающее, не произошел ли какой-либо HTTP-вызов на сайте, вы можете воспользоваться избыточностью, отправляя сообщение об ошибке из всех частей вашего приложения (или даже из общего промежуточного программного обеспечения).

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
}

Вам необходимо определить, как будет организовано ваше состояние и нужно ли переводить какое-то состояние в избыточное состояние или просто сохранять его в локальном состоянии вашего компонента. Вы могли бы поместить все в избыточность, но лично я бы сказал, что это излишне - зачем вам помещать состояние X в компонент Y, если только компонент Y заботится об этом состоянии? Если вы правильно структурируете свой код, у вас не должно возникнуть проблем с перемещением этого состояния из локального в избыточное в дальнейшем, если по какой-то причине другие части вашего приложения начнут зависеть от этого состояния.

Жубер
источник
1

Я думаю об этом, как это должно быть государство? А что должно быть выведено из состояния? Состояние должно храниться в редуксе, а деривации должны быть рассчитаны.

Номер телефона - это состояние, какое поле имеет фокус, это состояние, но независимо от того, действительно ли оно действительно, оно может быть получено из значений в состоянии.

Я бы использовал Reselect для кэширования дериваций и возврата тех же результатов, когда соответствующее состояние не было изменено.

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

Затем вы можете использовать селектор в mapStateToProps во всех компонентах, которые могут заботиться об этом значении, а также использовать его в асинхронных действиях. Если фокус не изменился или значение поля не изменилось, перерасчет не произойдет, вместо этого будет возвращено предыдущее значение.

Я добавляю выбранную проверку состояния только для того, чтобы показать, как несколько частей состояния могут объединиться и привести к одной деривации.

Я лично пытаюсь приблизиться к вещам, сохраняя свое состояние как можно меньше. Например, предположим, вы хотели создать свой собственный календарь. Будете ли вы хранить каждый день в состоянии, или вам просто нужно знать пару вещей, таких как текущий год и месяц, который просматривается в данный момент. Имея только эти 2 элемента состояния, вы можете рассчитать дни, отображаемые в календаре, и вам не нужно пересчитывать, пока один из них не изменится, и этот пересчет произойдет практически автоматически, не нужно думать обо всех способах, которыми они могли бы изменение.

насыщенный зеленый
источник