2016-05-06 16 views
15

Wykonuję żądania interfejsu API przy użyciu pobierania izomorficznego i używam Redux do obsługi stanu mojej aplikacji.Jak radzić sobie z błędami w odpowiedziach pobierania() z Redux Thunk?

Chcę obsłużyć zarówno błędy utraty połączenia internetowego, jak i błędy interfejsu API, uruchamiając akcje Redux.

Mam następujący (work-in-progress/złe) kodu, ale nie można dowiedzieć się w prawidłowy sposób, aby ogień działania Redux (zamiast po prostu wyrzucić błąd i zatrzymać wszystko):

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

Gdybym intionally wyłączyć połączenie z internetem w konsoli JS, gdy loguję poprzez console.log() (jak wyżej), to na wyjściu to: 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(…)

Wybacz mi, jeśli to jest całkowicie błędne, ale don nie chcesz robić nic poza odpaleniem dwóch akcji Redux, gdy wystąpi błąd (błąd ogólny i jedno działanie, które my ponowne wykonanie po wystąpieniu błędu).

Czy to, co próbuję osiągnąć, jest możliwe?

Wydaje się, że (przez mój logowania do konsoli) do „następnie” część skryptu jest nadal wykonywany (jako zawartość tego są moje „połów” funkcje dyspozytorskie) ..

Odpowiedz

48

Jestem mylić o kilka rzeczy:

  1. Dlaczego używasz Promise.all wokół wysyłania dwóch akcji synchronicznych? Wywołanie dispatch z czymś takim jak {type: PRE_FETCH_RESOURCES_FAIL, ...} nie zwróci obietnicy, więc Promise.all jest niepotrzebne. Promise.all() jest przydatny tylko wtedy, gdy wysyłane przez ciebie akcje są napisane jako twórcy akcji thunk, co nie ma miejsca w tym przypadku.
  2. return dispatch => ... jest potrzebny tylko raz na samym początku akcji twórców. Nie ma potrzeby powtarzania tego w blokach catch lub then - w rzeczywistości powtarzanie go powoduje, że wewnętrzny kod nie jest w ogóle wykonywany. To jest sposób na wstrzyknięcie dispatch do twojej funkcji na najwyższym poziomie i nie ma sensu jej powtarzania.
  3. Jeśli umieścisz then po catch, uruchomi się nawet po złapaniu błędu. Nie jest to zachowanie, które chcesz - nie ma sensu uruchamianie programu obsługi po wykonaniu procedury obsługi błędu. Chcesz, aby były dwiema oddzielnymi ścieżkami kodu.
  4. Drobne nazywanie nitpick: wywołujesz odpowiedź "req". Prawdopodobnie powinno to być res.

Wygląda na to, że masz zły umysłowy model działania Redux Thunk i próbujesz połączyć części różnych przykładów, aż kliknie. Losowe wcięcie również przyczynia się do tego, że ten kod jest trochę trudny do zrozumienia.

To będzie bolesne w przyszłości, więc zamiast tego sugeruję uzyskać bardziej kompletny model mentalny tego, co Redux Thunk ma na myśli, co oznacza return dispatch => ... i jak obietnice mieszczą się w obrazie. Polecam tę odpowiedź jako in-depth introduction to Redux Thunk.

Jeśli mamy rozwiązać te problemy, Twój kod powinien wyglądać mniej więcej tak zamiast:

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, dzięki za szczegółowe rozbiórkę. Czytanie wstępu do Redux Thunk "teraz. Wielkie dzięki! –

+0

@Dan Abramov co jeśli chcę wyodrębnić pobieranie w oddzielnym miejscu, wraz z "pobierz kompletnie nieudane (powiedzmy cors lub connectionoutout/odmówił)" catch, ale pozostaw bardziej specyficzny połów w akcji. Czy to możliwe? –

+0

@ Dan Abramov - Mam problemy z łańcuchem od obietnicy w tym scenariuszu. zawsze uruchamia funkcję rozstrzygniętą w łańcuchu, zamiast odrzuconej, kiedy jest w odrzecieniu zdecydowanie odrzucona, ponieważ moja akcja błędu jest wysyłana. myśli? –

-1

Rozwiązaniem było po prostu do (dla obu instancji rejestrowania błędów) wymienić:

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

z:

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

Zarówno '' Promise.reject' Promise.all' i są niepotrzebne. Chociaż technicznie może to sprawić, powoduje to, że kod jest bardziej skomplikowany, niż jest potrzebny, i nie służy żadnemu celowi. Proszę zobaczyć moją odpowiedź na szczegóły i sugestię przepisywania. –

Powiązane problemy