0

I'm learning React, along with pretty much all the necessary technology around it all at once - so I often get tripped up by things I should probably know already.

I've encountered a problem when it comes to error handling my async events. I've scoured the web and nothing really answers exactly what I'm looking for.

I'm currently using redux with redux-promise-middleware to handle the async actions, like this:

export function myFunc() {
  return {
    type: FETCH_FUNC,
    payload: new Promise((resolve, reject) => {
      fetch ('some/url/location/from/which/to/fetch')
        .then( response => {
          if (!response.ok){
            throw new Error(response);
            }
          resolve(response.json());
        }).catch(error => {
          reject(error);
        }),
    })
  };
}

There are two things here: first, the code works just fine when no errors are present. However, when I purposely create an error in the code the correct methods are firing but I still end up with the following error in my console:

Uncaught (in promise) Error: [object Response]

Should the .catch(...) block not be handling this? What am I missing? Should I be getting this anyway? If so, why?

Secondly, I've read that wrapping the fetch inside a new Promise is an anti-pattern, and there was an almost-hint that this may be what's causing problems here. All the examples I've come across use it in this fashion. What's the alternative? How do I fire the resolve/reject to dispatch the next actions without the wrapper?

Any help will be greatly appreciated. Thanks masters of the web.

-------------EDIT 1----------------

From the official redux-promise-middleware github examples, they have the following code:

export default function request(url, options) {
  return new Promise((resolve, reject) => {
    if (!url) reject(new Error('URL parameter required'));
    if (!options) reject(new Error('Options parameter required'));

    fetch(url, options)
      .then(response => response.json())
      .then(response => {
        if (response.errors) reject(response.errors);
        else resolve(response);
      })
      .catch(reject);
  });
}

It seems to intention with the middleware is to wrap fetch inside a new Promise and catching any rejects. If anyone has a working alternative way of implementing this using redux-promise-middleware, or can elaborate on why its following this pattern that would be greatly appreciated.

-------------EDIT 2----------------

Not sure what the intended way of implementing this is or how to avoid the Uncaught error in the promise. Simply calling Promise.reject(...) results in an uncaught error unless you include error handling functions: Promise.reject(...).then(() =>{...}, error => {...}). Including this with the middleware results in the rejected action never being dispatched. I've moved away from redux-promise-middleware till I can find a suitable fix and/or implementation.

4

3 回答 3

3

我猜你得到的是预期的结果,这在中间件文档中明确提到:

中间件分派被拒绝的动作,但不会捕获被拒绝的承诺。因此,您可能会在控制台中收到“未捕获”警告。这是未捕获的拒绝承诺的预期行为。捕获错误是你的责任,而不是 redux-promise-middleware 的责任。

但是,如果您询问最佳实践,这就是我很久以前最终做的事情,并且与我完美配合:

1- 对于某些承诺,您可以按照文档中的说明进行操作:

dispatch({
    type: 'FOO_ACTION',
    payload: new Promise(() => {
      throw new Error('foo');
    })
  }).catch(error => {
    // catch and handle error or do nothing
  });

2- 要全局捕获所有被拒绝的承诺,请在 redux-promise-middleware 之前添加此中间件,如下所示:

/**
 * a utility to check if a value is a Promise or not
 * @param value
 */
const isPromise = value => value !== null && typeof value === 'object' && typeof value.then === 'function';


export default () => {

  const middleWares = [];

  // global error middleware
  middleWares.push(() => next => action => {

    // If not a promise, continue on
    if (!isPromise(action.payload)) {
      return next(action);
    }

    /**
     * include a property in `meta and evaluate that property to check if this error will be handled locally
     *
     * if (!action.meta.localError) {
     *   // handle error
     * }
     *
     * The error middleware serves to dispatch the initial pending promise to
     * the promise middleware, but adds a `catch`.
     */
    if (!action.meta || !action.meta.localError) {
      // Dispatch initial pending promise, but catch any errors
      return next(action).catch(error => {
        if (config.showErrors) { // here you can decide to show or hide errors
          console.log(`${action.type} unhandled rejection caught at middleware with reason: ${JSON.stringify(error.message)}.`);
        }
        return error;
      });
    }

    return next(action);
  });

  // middleware
  middleWares.push(thunk);
  middleWares.push(promise());  
  middleWares.push(logger());

  return applyMiddleware(...middleWares);
}

我想这正是你要找的东西;)

另外,我强烈推荐axios不是 fetch,原因如下:

  • 如果请求有错误代码,则 axios 模块会自动拒绝承诺,这是您需要在 fetch 中手动处理的内容
  • 在 axios 中,您可以使用默认的 base-url、header、interceptors 创建实例...
  • 在 axios 中,您可以使用令牌取消任何先前的请求,这对于自动完成和聊天应用程序特别有用
  • 还有 axios 在内部自动切换xhrhttp模块以根据环境(NodeJs 或浏览器)执行 ajax 请求,我个人在电子、nodejs、浏览器和 react-native 中使用了相同的 redux 操作,一切正常
于 2017-05-23T15:52:18.757 回答
1

跟进 caisah 的评论,摆脱间接。您可以通过简单地使用新的承诺对象解决或拒绝来解决或拒绝承诺

export function myFunc() {
  return {
    type: FETCH_FUNC,
    payload: fetch ('some/url/location/from/which/to/fetch')
        .then(response => {
          if (!response.ok){
            throw new Error(response);
          }
          return Promise.resolve(response.json());
        }).catch(error => {
          return Promise.reject(error)
        }),
    })
  };
}

myFunc().payload.then(json => /* do stuff with json*/)

PS退货可能是多余的。

于 2017-02-28T17:32:54.747 回答
0

我使用了“捕获被拒绝的承诺引发的错误”中介绍的“全局捕获错误”,如图所示,调用 applyMiddleware 时,errorMiddleware 应该在 promiseMiddleware 之前。要过滤应用此中间件的操作类型,我首选正则表达式:

这是商店创建:

import { createStore, combineReducers, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise-middleware';
import errorMiddleware from './errorMiddleware';

import adultosReducer from './adultosReducer';

const rootReducer = combineReducers({
  adultosReducer
});

const composeStoreWithMiddleware = applyMiddleware(errorMiddleware, promiseMiddleware())(
  createStore
);

export default composeStoreWithMiddleware(rootReducer);

这是错误中间件:

import isPromise from 'is-promise';
import _ from 'lodash';

const tiposAction = /^ADULTO/i;

export default function errorMiddleware() {
  return next => action => {
    // If not a promise, continue on
    if (!isPromise(action.payload)) {
      return next(action);
    }

    console.log('errorMiddleware: action.type', action.type);
    if (action.type.match(tiposAction)) {
      // Dispatch initial pending promise, but catch any errors
      return next(action).catch(error => {
        console.log('catching action', error);

        return error;
      });
    }

    return next(action);
  };
}

这样你就可以温和地向用户显示错误,因为被拒绝的操作是在没有 Unhandled 承诺的情况下分派的。当然也不需要添加 redux-thunk。

于 2019-01-11T13:27:28.783 回答