2016-05-06 25 views
15

Sto facendo richieste API usando il recupero isomorfo e usando Redux per gestire lo stato della mia app.Come gestire gli errori nelle risposte fetch() con Redux Thunk?

Desidero gestire entrambi gli errori di perdita della connessione a Internet e gli errori API, disattivando le azioni di Redux.

Ho il seguente codice (work-in-progress/cattivo), ma non riesco a capire il modo corretto per sparare le azioni Redux (e non solo genera un errore e interrompere tutto):

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

Se intionally disabilitare la connessione Internet, nella console JS, quando accedo tramite console.log() (come sopra), è l'output di questo: 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(…)

perdonatemi se questo è del tutto sbagliato, ma io don Non voglio fare altro che sparare due Azioni Redux quando c'è un errore (un errore generale e uno specifico per l'azione ri esibire quando si è verificato l'errore).

È quello che sto cercando di raggiungere anche possibile?

Sembra che (tramite il mio registrazione per consolle) il 'poi' è ancora in corso di esecuzione parte dello script (come il contenuto di esso sono le mie funzioni di invio 'catturare') ..

risposta

48

sono confuso su diverse cose:

  1. Perché si utilizza Promise.all per l'invio di due azioni sincrone? Chiamare dispatch con qualcosa come {type: PRE_FETCH_RESOURCES_FAIL, ...} non restituirà una Promessa, quindi Promise.all non è necessario. Promise.all() è utile solo se le azioni inviate sono stesse scritte come creatori di azioni thunk, che non è il caso qui.
  2. return dispatch => ... è necessario solo una volta all'inizio dei creatori di azioni. Non è necessario ripeterlo nei blocchi catch o then, infatti, la ripetizione rende il codice interno non eseguito affatto. Questo è un modo per iniettare dispatch nella tua funzione al livello più alto, e non c'è motivo di ripeterlo.
  3. Se si inserisce then dopo un catch, verrà eseguito anche dopo che è stato rilevato un errore. Questo non è il comportamento che si desidera, non ha senso eseguire il gestore di successo subito dopo il gestore degli errori. Vuoi che siano due percorsi di codice separati.
  4. Nitidezza minore per la denominazione: si sta chiamando la risposta a "req". Probabilmente dovrebbe essere res.

Sembra che tu abbia un modello mentale errato di come funziona Redux Thunk, e stai cercando di combinare parti di diversi esempi insieme finché non scatta. Anche l'indentazione casuale contribuisce a rendere questo codice un po 'difficile da capire.

In futuro questo sarà doloroso quindi suggerisco di ottenere un modello mentale più completo di ciò che fa Redux Thunk, che cosa significa return dispatch => ... e in che modo Promises si adatta all'immagine. Vorrei raccomandare questa risposta come in-depth introduction to Redux Thunk.

Se fissiamo questi problemi, il codice dovrebbe apparire più o meno come questo, invece:

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'}) 
     } 
    ); 
    } 
} 
+1

Wow, grazie per la dissezione dettagliata. Leggendo la Redux Thunk intro 'adesso. Molte grazie! –

+0

@Dan Abramov Cosa succede se voglio estrarre il recupero in un posto separato, insieme al fermo "fetch completamente fallito (ad esempio cors o connection timeout/refused)", ma lasciare la cattura più specifica nell'azione. È possibile? –

+0

@Dan Abramov - Sto riscontrando problemi nel mantenere una promessa in questo scenario. attiva sempre la funzione risolta nella catena anziché quella rifiutata, quando nel thunk viene definitivamente rifiutata dal momento in cui viene inviata la mia azione di errore. pensieri? Aggiornamento –

-1

La soluzione è stata semplicemente a (per entrambe le istanze di registrazione degli errori) sostituire:

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'}) 
])``` 

Con:

return 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'}), 
Promise.reject(err) 
]) 
+1

Sia '' Promise.all' e Promise.reject' sono inutili. Sebbene sia tecnicamente possibile farlo funzionare, questo sta rendendo il codice più complicato di quanto dovrebbe essere e non serve a nulla. Si prega di vedere la mia risposta per i dettagli e un suggerimento di riscrittura. –

Problemi correlati