web-dev-qa-db-fra.com

Quelle est la meilleure façon de traiter une erreur d'extraction dans react redux?

J'ai un réducteur pour les clients, un autre pour AppToolbar et quelques autres ...

Disons maintenant que j'ai créé une action d'extraction pour supprimer le client et que, si cela échoue, le code dans le réducteur Clients doit être rempli, mais je souhaite également afficher une erreur globale dans AppToolbar.

Mais les clients et les réducteurs AppToolbar ne partagent pas la même partie de l'état et je ne peux pas créer une nouvelle action dans le réducteur.

Alors, comment puis-je montrer une erreur globale? Merci

UPDATE 1:

J'oublie de mentionner que j'utilise este devstack

UPDATE 2: J'ai marqué la réponse d'Eric comme étant correcte, mais je dois dire que la solution que j'utilise in este ressemble plus à une combinaison de la réponse d'Eric et de Dan ... ce qui vous va le mieux dans votre code ...

74
Dusan Plavak

Si vous souhaitez avoir le concept "d'erreurs globales", vous pouvez créer un réducteur errors, capable d'écouter les actions addError, removeError, etc .... Ensuite, vous pouvez vous connecter à l’arbre d’état Redux à l’aide de state.errors et les afficher le cas échéant.

Vous pouvez aborder ce problème de différentes manières, mais l’idée générale est que les erreurs/messages globaux mériteraient que leur propre réducteur vive de manière totalement distincte de <Clients /><AppToolbar />. Bien sûr, si l’un ou l’autre de ces composants nécessite un accès à errors, vous pouvez lui passer errors comme accessoire si nécessaire.

_/Mise à jour: exemple de code

Voici un exemple de ce à quoi cela pourrait ressembler si vous deviez passer les "erreurs globales" errors dans votre <App /> de niveau supérieur et les restituer sous forme conditionnelle (si des erreurs étaient présentes). Utiliser connect de react-redux pour connecter votre composant <App /> à certaines données.

// 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);

Et en ce qui concerne le créateur de l'action, il enverrait un ( redux-thunk ) échec de succès en fonction de la réponse.

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});
      });
  };
}

Bien que votre réducteur puisse simplement gérer un tableau d’erreurs, ajouter/supprimer des entrées de manière appropriée.

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;
  }
}
89
Erik Aybar

La réponse d’Erik est correcte, mais j’aimerais ajouter qu’il n’est pas nécessaire de déclencher des actions distinctes pour ajouter des erreurs. Une autre approche consiste à avoir un réducteur qui gère toute action avec un champ error. C'est une question de choix personnel et de convention.

Par exemple, à partir de Redux real-world exemple qui contient une gestion des erreurs:

// 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
}
88
Dan Abramov

L'approche que je suis en train de prendre pour quelques erreurs spécifiques (validation de l'entrée utilisateur) consiste à demander à mes sous-réducteurs de lever une exception, de la récupérer dans mon réducteur de racine et de la joindre à l'objet d'action. Ensuite, j'ai une saga redux-saga qui inspecte les objets d'action pour rechercher une erreur et met à jour l'arbre d'état avec les données d'erreur dans ce cas.

Alors:

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));
  }
}

Et puis, ajouter l'erreur à l'arbre d'état est décrit par Erik.

Je l'utilise avec parcimonie, mais cela m'empêche de dupliquer la logique qui appartient légitimement au réducteur (afin qu'il puisse se protéger d'un état non valide).

1
Gavin

ce que je fais est je centralise toute la gestion des erreurs dans l'effet sur une base par effet

/**
 * 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}`));
    });
0
meanstack

écrivez un middleware personnalisé pour gérer toutes les erreurs liées à l'API. Dans ce cas, votre code sera plus propre.

   failure/ error actin type ACTION_ERROR

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

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

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

       }
}
0
Khalid Azam