web-dev-qa-db-fra.com

Comment gérer les erreurs dans les réponses fetch () avec Redux Thunk?

Je fais des demandes d'API en utilisant la récupération isomorphe et en utilisant Redux pour gérer l'état de mon application.

Je veux gérer à la fois les erreurs de perte de connexion Internet et les erreurs d'API, en déclenchant les actions Redux.

J'ai le code suivant (travail en cours/mauvais), mais je ne peux pas trouver la bonne façon de déclencher les actions Redux (plutôt que de simplement lancer une erreur et de tout arrêter):

export function createPost(data = {}) {

    return dispatch => {

        dispatch(requestCreatePost(data))

        return fetch(API_URL + data.type, {
            credentials: 'same-Origin',
            method: 'post',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json',
                'X-WP-Nonce': API.nonce
            },
            body: JSON.stringify(Object.assign({}, data, {status: 'publish'}))
        }).catch((err) => {

            //HANDLE WHEN HTTP ISN'T EVEN WORKING
            return dispatch => Promise.all([
                dispatch({type: PRE_FETCH_RESOURCES_FAIL, errorType: 'fatal', message:'Error fetching resources', id: h.uniqueId()}),
                dispatch({type: PRE_CREATE_API_ENTITY_ERROR, errorType: 'fatal', id: h.uniqueId(), message: 'Entity error before creating'})
            ])
        }).then((req) => {

            //HANDLE RESPONSES THAT CONSTITUTE AN ERROR (VIA THEIR HTTP STATUS CODE)
            console.log(req);
            if (!req || req.status >= 400) {
                return dispatch => Promise.all([
                    dispatch({type: FETCH_RESOURCES_FAIL, errorType: 'warning', message:'Error after fetching resources', id: h.uniqueId()}),
                    dispatch({type: CREATE_API_ENTITY_ERROR, errorType: 'warning', id: h.uniqueId(), message: 'Entity error whilst creating'})
                ])
            }
            else {
                return req.json()
            }
        }).then((json) => {
            var returnData = Object.assign({},json,{
                type: data.type
            });
            dispatch(receiveCreatePost(returnData))
        })
    }
}

Si je désactive la connexion Internet, dans la console JS, lorsque je me connecte via console.log () (comme ci-dessus), cela génère: POST http://example.com/post net::ERR_INTERNET_DISCONNECTED(anonymous function) (dispatch) { return Promise.all([dispatch({ type: PRE_FETCH_RESOURCES_FAIL, errorType: 'fatal', message: 'Error fetching resources', id: _CBUtils2.default.uniqueId() }), dispatch({ type:… cb_app_scripts.js?ver=1.0.0:27976 Uncaught (in promise) TypeError: req.json is not a function(…)

Pardonnez-moi si c'est complètement faux, mais je ne veux rien faire d'autre que de déclencher deux actions Redux lorsqu'il y a une erreur (une erreur générale, et une spécifique à l'action que nous effectuions lorsque l'erreur s'est produite).

Est-ce que ce que j'essaie de réaliser est même possible?

Il semble que (via ma connexion à la console) la partie 'puis' du script soit toujours en cours d'exécution (car le contenu de celui-ci sont mes fonctions de répartition 'catch').

18
Lloyd Jones

Je suis confus à propos de plusieurs choses:

  1. Pourquoi utilisez-vous Promise.all Pour distribuer deux actions synchrones? Appeler dispatch avec quelque chose comme {type: PRE_FETCH_RESOURCES_FAIL, ...} Ne retournera pas de promesse, donc Promise.all N'est pas nécessaire. Promise.all() n'est utile que si les actions que vous distribuez sont elles-mêmes écrites en tant que créateurs d'actions thunk, ce qui n'est pas le cas ici.
  2. return dispatch => ... N'est nécessaire qu'une seule fois au tout début des créateurs d'action. Il n'est pas nécessaire de répéter cela dans les blocs catch ou then - en fait, le répéter fait que le code interne ne s'exécute pas du tout. C'est une façon d'injecter dispatch dans votre fonction au niveau supérieur, et il est inutile de la répéter.
  3. Si vous mettez then après un catch, il s'exécutera même après qu'une erreur a été détectée. Ce n'est pas le comportement que vous souhaitez, cela n'a aucun sens d'exécuter le gestionnaire de réussite juste après le gestionnaire d'erreur. Vous voulez qu'ils soient deux chemins de code distincts.
  4. Mineur de nommage mineur: vous appelez la réponse "req". Ce devrait probablement être res.

Il semble que vous ayez un mauvais modèle mental du fonctionnement de Redux Thunk et que vous essayez de combiner des parties de différents exemples jusqu'à ce qu'il clique. L'indentation aléatoire contribue également à ce que ce code soit un peu difficile à comprendre.

Cela va être douloureux à l'avenir, donc je suggère plutôt d'obtenir un modèle mental plus complet de ce que fait Redux Thunk, de ce que return dispatch => ... Signifie et de la façon dont les promesses s'inscrivent dans l'image. Je recommanderais cette réponse comme introduction approfondie à Redux Thunk .

Si nous résolvons ces problèmes, votre code devrait plutôt ressembler à ceci:

export function createPost(data = {}) {
  return dispatch => {
    dispatch(requestCreatePost(data));

    return fetch(API_URL + data.type, {
      credentials: 'same-Origin',
      method: 'post',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'X-WP-Nonce': API.nonce
      },
      body: JSON.stringify(Object.assign({}, data, {status: 'publish'}))
    })
    // Try to parse the response
    .then(response =>
      response.json().then(json => ({
        status: response.status,
        json
      })
    ))
    .then(
      // Both fetching and parsing succeeded!
      ({ status, json }) => {
        if (status >= 400) {
          // Status looks bad
          dispatch({type: FETCH_RESOURCES_FAIL, errorType: 'warning', message:'Error after fetching resources', id: h.uniqueId()}),
          dispatch({type: CREATE_API_ENTITY_ERROR, errorType: 'warning', id: h.uniqueId(), message: 'Entity error whilst creating'})
        } else {
          // Status looks good
          var returnData = Object.assign({}, json, {
              type: data.type
          });
          dispatch(receiveCreatePost(returnData))
        }
      },
      // Either fetching or parsing failed!
      err => {
        dispatch({type: PRE_FETCH_RESOURCES_FAIL, errorType: 'fatal', message:'Error fetching resources', id: h.uniqueId()}),
        dispatch({type: PRE_CREATE_API_ENTITY_ERROR, errorType: 'fatal', id: h.uniqueId(), message: 'Entity error before creating'})
      }
    );
  }
}
52
Dan Abramov