web-dev-qa-db-fra.com

redux-promesse avec Axios, et comment faire face aux erreurs?

Donc, je vois une erreur, redux-promise me remet une erreur: vrai, avec la charge utile, mais c'est une fois qu'il frappe le réducteur ... pour moi, découpler la demande ET la condition d'erreur est un peu étrange, et semble inapproprié. Quel est un moyen efficace de traiter également les conditions d'erreur lors de l'utilisation d'axios avec reduc-promise (middleware) .. voici l'essentiel de ce que j'ai ..

in action/
const request = axios(SOME_URL);

return {
   type: GET_ME_STUFF,
   payload: request
}

in reducer/
  const startState = {
     whatever: [],
     error: false
  }

  case GET_ME_STUFF:
     return {...state, startState, {stuff:action.payload.data, error: action.error? true : false}}

etc ... alors je peux gérer l'erreur .. donc, mon appel api est maintenant divisé en deux zones distinctes et cela semble faux .... il doit y avoir quelque chose qui me manque ici. Je pense que dans les actions /, je peux passer un rappel qui gère une nouvelle action, etc. ou quelque chose, mais pas le diviser.

10
james emanon

J'ai dû traverser une situation similaire. Le défi est que vous ne pourrez probablement pas évaluer les résultats de la promesse avant qu'elle ne soit au réducteur. Vous pourriez y gérer vos exceptions mais ce n'est pas le meilleur modèle. D'après ce que j'ai lu, les réducteurs sont uniquement destinés à renvoyer des éléments d'état appropriés basés sur action.type et à ne rien faire d'autre.

Entrez donc un middleware supplémentaire, redux-thunk. Au lieu de renvoyer un objet, il renvoie une fonction et peut coexister avec promesse.

C'est assez bien expliqué sur http://danmaz74.me/2015/08/19/from-flux-to-redux-async-actions-the-easy-way/ [archivé ici] . Essentiellement, vous pouvez évaluer la promesse ici et l'envoyer par les autres créateurs d'action avant que le résultat de la promesse n'atteigne les réducteurs.

Dans votre fichier d'actions, ajoutez des créateurs d'actions supplémentaires qui géreraient les états de réussite et d'erreur (et tout autre).

function getStuffSuccess(response) {
  return {
    type: GET_ME_STUFF_SUCCESS,
    payload: response
  }
}

function getStuffError(err) {
  return {
    type: GET_ME_STUFF_ERROR,
    payload: err
  }
}

export function getStuff() {
  return function(dispatch) {
    axios.get(SOME_URL)
      .then((response) => {
        dispatch(getStuffSuccess(response))
      })
      .catch((err) => {
        dispatch(getStuffError(err))
      })
  }
}

return null

C'est à peu près comment vous pouvez traduire votre pseudocode en ce qui est expliqué sur le lien. Cela gère l'évaluation de la promesse directement dans votre créateur d'action et le déclenchement des actions et charges utiles appropriées vers vos réducteurs, conformément à la convention d'action -> réducteur -> état -> cycle de mise à jour des composants. Je suis moi-même encore assez nouveau dans React/Redux mais j'espère que cela vous aidera.

21
Victor

La réponse acceptée ne fait pas usage de redux-promesse. Étant donné que la question porte sur la gestion des erreurs à l'aide de redux-promise, je fournis une autre réponse.

Dans le réducteur, vous devez inspecter l'existence de l'attribut error sur l'objet action:

// This is the reducer
export default function(previousState = null, action) {
  if (action.error) {
    action.type = 'HANDLE_XHR_ERROR'; // change the type
  }  
  switch(action.type) {
    ...

Et changez le type de l'action, déclenchant un changement d'état pour un composant de gestion des erreurs que vous avez configuré pour cela.

Vous pouvez en lire un peu plus à ce sujet ici sur github.

11
pors

Toujours en utilisant les promesses de redux, vous pouvez faire quelque chose comme ça qui, je pense, est une manière élégante de résoudre ce problème.

Tout d'abord, définissez une propriété dans l'état redux qui contiendra toutes les erreurs ajax qui pourraient se produire.

ajaxError: {},

Deuxièmement, configurez un réducteur pour gérer les erreurs ajax:

export default function ajaxErrorsReducer(state = initialState.ajaxError, action) {
  if (action.error) {
    const { response } = action.payload;
    return {
      status: response.status,
      statusText: response.statusText,
      message: response.data.message,
      stack: response.data.stack,
    };
  }
  return state;
}

Enfin, créez un composant react très simple qui rendra les erreurs s'il y en a (j'utilise la bibliothèque react-s-alert pour afficher les alertes Nice):

import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import Alert from 'react-s-alert';

class AjaxErrorsHandler extends Component {

  constructor(props, context) {
    super(props, context);
    this.STATUS_GATE_WAY_TIMEOUT = 504;
    this.STATUS_SERVICE_UNAVAILABLE = 503;
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.ajaxError !== nextProps.ajaxError) {
      this.showErrors(nextProps.ajaxError);
    }
  }

  showErrors(ajaxError) {
    if (!ajaxError.status) {
      return;
    }
    Alert.error(this.getErrorComponent(ajaxError), {
      position: 'top-right',
      effect: 'jelly',
      timeout: 'none',
    });
  }

  getErrorComponent(ajaxError) {
    let customMessage;
    if (
      ajaxError.status === this.STATUS_GATE_WAY_TIMEOUT ||
      ajaxError.status === this.STATUS_SERVICE_UNAVAILABLE
    ) {
      customMessage = 'The server is unavailable. It will be restored very shortly';
    }
    return (
      <div>
        <h3>{ajaxError.statusText}</h3>
        <h5>{customMessage ? customMessage : ajaxError.message}</h5>
      </div>
    );
  }

  render() {
    return (
      <div />
    );
  }

}

AjaxErrorsHandler.defaultProps = {
  ajaxError: {},
};

AjaxErrorsHandler.propTypes = {
  ajaxError: PropTypes.object.isRequired,
};

function mapStateToProps(reduxState) {
  return {
    ajaxError: reduxState.ajaxError,
  };
}

export default connect(mapStateToProps, null)(AjaxErrorsHandler);

Vous pouvez inclure ce composant dans votre composant d'application.

1
juank11memphis

Il semble que vous puissiez détecter l'erreur à l'endroit où vous effectuez la répartition, puis effectuer une répartition d'erreur distincte si cela se produit. C'est un peu un hack mais ça marche.

  store.dispatch (function (dispatch) {
      dispatch ({
        type:'FOO',
        payload:axios.get(url)
      })
      .catch (function(err) {
        dispatch ({
          type:"FOO" + "_REJECTED",
          payload:err
        });
      });
  });

et dans le réducteur

const reducer = (state=initialState, action) => {
  switch (action.type) {
    case "FOO_PENDING": {
      return {...state, fetching: true};
    }
    case "FOO_REJECTED": {
      return {...state, fetching: false, error: action.payload};
    }
    case "FOO_FULFILLED": {
      return {
        ...state,
        fetching: false,
        fetched: true,
        data: action.payload,
      };
    }
  }
  return state;
};
1
bruce

Ce n'est peut-être pas la meilleure approche, mais cela fonctionne pour moi. Je passe le "ceci" de mon composant comme contexte var. Ensuite, lorsque je reçois une réponse, j'exécute simplement les méthodes définies dans le contexte de mes composants. Dans mon composant, j'ai successHdl et errorHdl. De là, je peux déclencher plus d'actions redux comme d'habitude. J'ai vérifié toutes les réponses précédentes et semble trop intimidant pour une tâche aussi triviale.

export function updateJob(payload, context){

    const request = axios.put(UPDATE_SOMETHING, payload).then(function (response) {
        context.successHdl(response);
    })
    .catch(function (error) {
        context.errorHdl(error);
    });;

    return {
        type: UPDATE_SOMETHING,
        payload: payload,
    }
}
0
Roberto Aguilar