Який найкращий спосіб впоратися з помилкою вибору в реакції редукції?


105

У мене є один редуктор для клієнтів, ще один для AppToolbar та деякі інші ...

Тепер давайте скажемо, що я створив дію для вилучення клієнта, і якщо він не вдається, у мене є код у редукторі клієнтів, який повинен робити деякі речі, але також я хочу показати деяку глобальну помилку в панелі AppTool.

Але Клієнти та редуктори панелі AppTool не поділяють однакову частину стану, і я не можу створити нову дію в редукторі.

Тож як я можу показати глобальну помилку? Дякую

ОНОВЛЕННЯ 1:

Забуваю згадати, що я використовую este devstack

ОНОВЛЕННЯ 2: Я позначив відповідь Еріка як правильну, але я мушу сказати, що рішення, яке я використовую, більше нагадує поєднання відповіді Еріка та Дена ... Вам просто потрібно знайти те, що найкраще відповідає вашому коду .. .


2
Ви наближаєтесь до голосів, і це, мабуть, тому, що ви не надаєте багато прикладного коду. Ваше питання та відповіді, які ви отримаєте, будуть кориснішим іншим, якщо проблема буде викладена чіткіше.
acjay

Я повинен погодитись w / @acjay, що це питання бракує контексту. Я відповів нижче (із прикладами коду) із загальним рішенням, але ваше запитання може використати певне уточнення. Схоже, у вас може виникнути кілька окремих питань. 1) Поводження з асинхронними діями / помилками. 2) Розподілити стан належним чином у вашому стані відновного стану. 3) Отримання ваших компонентів необхідних даних.
Ерік Айбар

@ErikTheDeveloper спасибі, ваша відповідь виглядає чудово. Але ти маєш рацію, я забув згадати контекст. Я відредагував своє запитання, я використовую este devstack, і, схоже, ваша відповідь там не застосовується, як є ...
Душан Плавак

Відповіді:


116

Якщо ви хочете мати поняття "глобальні помилки", ви можете створити errorsредуктор, який може прослуховувати дії addError, removeError тощо .... Потім ви можете підключити до свого дерева стану Redux у state.errorsта показати їх, де це доречно.

Ви можете підійти до цього декількома способами, але загальна ідея полягає в тому, що глобальні помилки / повідомлення заслуговують на те, щоб їх власний редуктор жив повністю окремо від <Clients />/ <AppToolbar />. Звичайно, якщо будь-який з цих компонентів потребує доступу до errorsвас, ви можете перейти errorsдо них як опора, куди потрібно.

Оновлення: Приклад коду

Ось один приклад того, як це могло б виглядати, якби ви передавали «глобальні помилки» errorsна свій найвищий рівень <App />і умовно їх рендерували (якщо є помилки). Використання react-redux'sconnect для підключення вашого <App />компонента до деяких даних.

// App.js
// Display "global errors" when they are present
function App({errors}) {
  return (
    <div>
      {errors && 
        <UserErrors errors={errors} />
      }
      <AppToolbar />
      <Clients />
    </div>
  )
}

// Hook up App to be a container (react-redux)
export default connect(
  state => ({
    errors: state.errors,
  })
)(App);

І наскільки творець дії, то воно буде відправляти ( перевождь-перетворювач ) відмова успіху в відповідно до відповіддю

export function fetchSomeResources() {
  return dispatch => {
    // Async action is starting...
    dispatch({type: FETCH_RESOURCES});

    someHttpClient.get('/resources')

      // Async action succeeded...
      .then(res => {
        dispatch({type: FETCH_RESOURCES_SUCCESS, data: res.body});
      })

      // Async action failed...
      .catch(err => {
        // Dispatch specific "some resources failed" if needed...
        dispatch({type: FETCH_RESOURCES_FAIL});

        // Dispatch the generic "global errors" action
        // This is what makes its way into state.errors
        dispatch({type: ADD_ERROR, error: err});
      });
  };
}

Тоді як ваш редуктор може просто керувати масивом помилок, додаючи / видаляючи записи відповідним чином.

function errors(state = [], action) {
  switch (action.type) {

    case ADD_ERROR:
      return state.concat([action.error]);

    case REMOVE_ERROR:
      return state.filter((error, i) => i !== action.index);

    default:
      return state;
  }
}

1
Еріку, у мене є щось подібне до того, що ти запропонував тут, але дивно, що мені ніколи не вдається отримати catchфункції, викликані, someHttpClient.get('/resources')або якщо fetch('/resources')я використовую для повернення коду 500 Server Error. Чи є у вас якісь думки вгорі голови, де я можу помилитися? По суті, те, що я роблю, - fetchце надіслати запит, який закінчується моїм, в routesякому я викликаю метод на своїй mongooseмоделі, щоб зробити щось дуже просте, наприклад додавання тексту або видалення тексту з БД.
Кевін Габусі

2
Гей, я прийшов сюди з пошуку в Google - просто хотів подякувати вам за прекрасний приклад .. Я боровся з тими ж проблемами, і це геніально. Звичайно рішення - інтегрувати помилки в магазин. Чому я не подумав про це ... ура
Спок

2
Як би виконати функцію, коли виникає помилка? наприклад, мені потрібно показати тост / сповіщення в інтерфейсі, а не відображати компонент сповіщення, оновивши реквізити батьківського компонента
Gianfranco P.

111

Відповідь Еріка правильна, але я хотів би додати, що вам не потрібно вводити окремі дії для додавання помилок. Альтернативний підхід - мати редуктор, який обробляє будь-які дії з errorполем . Це питання особистого вибору та умовності.

Наприклад, з прикладу Redux,real-world який має обробку помилок:

// Updates error message to notify about the failed fetches.
function errorMessage(state = null, action) {
  const { type, error } = action

  if (type === ActionTypes.RESET_ERROR_MESSAGE) {
    return null
  } else if (error) {
    return error
  }

  return state
}

Чи означає це, що для кожного запиту на успіх ми повинні передавати тип RESET_ERROR_MESSAGE на редуктор errorMessage?
Дімі Мікадзе

2
@DimitriMikadze ні це не робить. Ця функція є просто скорочувачем для стану помилок. Якщо ви пройдете RESET_ERROR_MESSAGE, він очистить усі повідомлення про помилки. Якщо ви не пройдете і немає поля помилок, воно просто повернеться в незміненому стані, тож якщо були помилки з попередніх дій, вони все одно будуть там після успішних дій ....
Душан Плавак

Я віддаю перевагу такому підходу, оскільки він забезпечує більш природну вбудовану реакцію, оскільки споживач додає errorдо корисного навантаження дії. Дякую Ден!
Майк Перренод

1
Я не можу з головою зрозуміти, як це працює. Окрім прикладу реального світу, чи є у вас поодинокі документи та відео, які пояснюють це? Це досить основна вимога більшості проектів, і я знайшов мало простого для розуміння документації з цього питання. Дякую.
Метт Сондерс

6
@MattSaunders Намагаючись зрозуміти це, я натрапив на курс Redux від самого Дена (відповідача, який насправді творець Redux), з розділом « Відображення повідомлень про помилки», який разом з цими відповідями та прикладом у реальному світі привів його додому я. Удачі.
Агустін Ладо

2

Підхід, який я зараз приймаю за декілька конкретних помилок (перевірка введення користувачем), полягає в тому, щоб мої субредуктори відкидали виняток, зафіксували його в моєму кореневому редукторі та додали його до об'єкта дії. Тоді у мене є редукс-сага, яка перевіряє об’єкти дій на предмет помилки та оновлює дерево стану з даними про помилки в цьому випадку.

Так:

function rootReducer(state, action) {
  try {
    // sub-reducer(s)
    state = someOtherReducer(state,action);
  } catch (e) {
    action.error = e;
  }
  return state;
}

// and then in the saga, registered to take every action:
function *errorHandler(action) {
  if (action.error) {
     yield put(errorActionCreator(error));
  }
}

А потім додавання помилки до дерева стану відбувається так, як описує Ерік.

Я використовую його досить щадно, але це не дає мені дублювати логіку, яка легітимно належить до редуктора (щоб він міг захистити себе від недійсного стану).


1

написати спеціальне середнє програмне забезпечення для обробки всіх помилок, пов’язаних з api. У цьому випадку ваш код стане більш чистим.

   failure/ error actin type ACTION_ERROR

   export default  (state) => (next) => (action) => {

      if(ACTION_ERROR.contains('_ERROR')){

       // fire error action
        store.dispatch(serviceError());

       }
}

1
Також ускладнює налагодження IMHO
chrisjlee

2
Для цього вам не потрібно проміжне програмне забезпечення, ви можете записати таке саме ifв редуктор
Juan Campa

Якщо є більше 50 апі, то вам потрібно писати в будь-якому місці. Натомість ви можете написати власні проміжні програми, щоб перевірити помилку.
Шраван

0

те, що я роблю, - це централізувати всі поводження з помилками в результаті на основі ефекту

/**
 * central error handling
 */
@Effect({dispatch: false})
httpErrors$: Observable<any> = this.actions$
    .ofType(
        EHitCountsActions.HitCountsError
    ).map(payload => payload)
    .switchMap(error => {
        return of(confirm(`There was an error accessing the server: ${error}`));
    });

-8

Ви можете використовувати axios HTTP-клієнт. У ньому вже є реалізована функція перехоплювачів. Ви можете перехоплювати запити або відповіді, перш ніж вони потім будуть оброблені, або зловити.

https://github.com/mzabriskie/axios#interceptors

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });


Так, але ви нічого не відправляєте на скорочення?
Ейно Макітало

Цей підхід непоганий. Зазвичай зберігання у скороченні є однотонним, і ви можете імпортувати зберігання у файл перехоплювачів axios та використовувати store.dispatch () для запуску будь-яких дій. Це єдиний підхід для обробки всіх помилок api в системі на 1 місці
Wedmich
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.