处理 redux-promise-middleware 时出错

Error handling redux-promise-middleware

我正在学习 React,同时学习几乎所有必要的技术 - 所以我经常被我可能已经知道的事情绊倒。

我在处理异步事件时遇到错误。我已经在网上搜索过,但没有任何内容能准确回答我正在寻找的内容。

我目前正在使用 redux 和 redux-promise-middleware 来处理异步操作,如下所示:

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

这里有两件事:首先,代码在没有错误的情况下工作正常。但是,当我故意在代码中创建错误时,正确的方法正在触发,但我的控制台中仍然出现以下错误:

Uncaught (in promise) Error: [object Response]

.catch(...) 块不应该处理这个吗?我错过了什么?我应该得到这个吗?如果是,为什么?

其次,我读到将获取包装在一个新的 Promise 中是一种反模式,并且几乎暗示这可能是导致问题的原因。我遇到的所有示例都以这种方式使用它。还有什么选择?如何触发 resolve/reject 以在没有包装器的情况下分派下一个操作?

任何帮助将不胜感激。感谢网络高手。

------------编辑 1----------------

来自官方的redux-promise-middlewaregithub例子,他们有如下代码:

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

中间件的意图似乎是将 fetch 包装在 new Promise 中并捕获任何 reject。如果有人有使用 redux-promise-middleware 实现此功能的替代方法,或者可以详细说明为什么遵循此模式,我们将不胜感激。

------------编辑 2----------------

不确定实现此目的的预期方式是什么或如何避免承诺中的未捕获错误。除非包含错误处理函数,否则简单地调用 Promise.reject(...) 会导致未捕获的错误:Promise.reject(...).then(() =>{...}, error => {...})。将其包含在中间件中会导致被拒绝的操作永远不会被发送。我已经离开 redux-promise-middleware 直到找到合适的修复 and/or 实现。

跟进 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*/)

P.S returns 可能是多余的。

我想你得到的是预期的结果,中间件中清楚地提到了这一点 documentation:

The middleware dispatches rejected actions but does not catch rejected promises. As a result, you may get an "uncaught" warning in the console. This is expected behavior for an uncaught rejected promise. It is your responsibility to catch the errors and not the responsibility of 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);
}

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

Extra 我强烈建议 axios over fetch,原因如下:

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

我使用了"Catching Errors Thrown by Rejected Promises"中的"Catching Errors Globally",如图所示,当调用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.