Redux-promise与Axios一起使用,如何处理错误?

10

因此,我在一个错误上看到了redux-promise将错误信息:true和有效载荷一起返回给我,但这是一旦它到达reducer...对我来说,分离请求和错误条件有点奇怪,并且似乎不恰当。在使用axios w/ reduc-promise(中间件)时,处理错误条件的有效方法是什么?这是我所拥有的要点。

in action/
const request = axios(SOME_URL);

return {
   type: GET_ME_STUFF,
   payload: request
}

in reducer/
  const startState = {
     whatever: [],
     error: false
  }

  case GET_ME_STUFF:
     return {...state, startState, {stuff:action.payload.data, error: action.error? true : false}}

等等...那么我可以处理这个错误...所以,我的API调用现在分成了两个独立的部分,这似乎是错误的...我肯定是漏掉了什么。我认为在/actions中我可以传递一个回调函数来处理新的操作等等,但不是把它分成两个。

6个回答

22

我曾经遇到过类似的情况。挑战在于,直到 reducer 处理才能评估 promise 的结果。你可以在那里处理异常,但这不是最佳模式。据我所知,reducer 只是根据 action.type 返回适当的状态片段,不做其他事情。

因此,需要一个额外的中间件 redux-thunk。它返回函数而不是对象,可以与 promise 共存。

详细解释请访问http://danmaz74.me/2015/08/19/from-flux-to-redux-async-actions-the-easy-way/ (存档在这里)。基本上,在这里可以评估 promise 并通过其他 action creator 调度,然后才会传递给 reducers 处理。

在 actions 文件中,添加额外的 action creator 来处理成功、错误(和其他)状态。

function getStuffSuccess(response) {
  return {
    type: GET_ME_STUFF_SUCCESS,
    payload: response
  }
}

function getStuffError(err) {
  return {
    type: GET_ME_STUFF_ERROR,
    payload: err
  }
}

export function getStuff() {
  return function(dispatch) {
    axios.get(SOME_URL)
      .then((response) => {
        dispatch(getStuffSuccess(response))
      })
      .catch((err) => {
        dispatch(getStuffError(err))
      })
  }
}

return null

这大致是如何将您的伪代码翻译成链接中所解释的内容。此操作直接在您的操作创建程序中评估承诺并触发相应的操作和有效载荷传递到您的约简器,遵循操作->约简器->状态->组件更新周期的约定。我自己对React/Redux还很陌生,但希望能有所帮助。


@FirstOne 我添加了到Wayback Machine存档的链接。 - Victor

12

被接受的答案并没有使用redux-promise。既然这个问题实际上是关于如何使用redux-promise处理错误,我提供另一个答案。

在reducer中,您应该检查action对象上的error属性是否存在:

// This is the reducer
export default function(previousState = null, action) {
  if (action.error) {
    action.type = 'HANDLE_XHR_ERROR'; // change the type
  }  
  switch(action.type) {
    ...

并更改操作类型,触发您为此设置的错误处理组件的状态更改。

您可以在这里的 Github 上阅读更多相关信息。


2
这真的很有帮助,我现在要使用这个方法。请注意,似乎您不应该更改减速器中的操作:http://redux.js.org/docs/Troubleshooting.html#never-mutate-reducer-arguments - freb
@freb 是的,你说得对。我现在正在使用 redux-promise-middleware,它可以很好地处理错误。 - pors
你说得对,redux-promise-middleware 在错误处理方面看起来要好得多。感谢你的指引! - freb
1
在 switch 语句之外放置这个是一个非常糟糕的想法。如果其他操作出现错误会怎样呢?这也会导致这个 case(以及你类似实现的其他 case)被执行。 - Galupuf

1

看起来你可以在派发时捕获错误,然后如果发生错误,再进行单独的错误派发。这有点像 hack,但是它确实有效。

  store.dispatch (function (dispatch) {
      dispatch ({
        type:'FOO',
        payload:axios.get(url)
      })
      .catch (function(err) {
        dispatch ({
          type:"FOO" + "_REJECTED",
          payload:err
        });
      });
  });

并且在 reducer 中

const reducer = (state=initialState, action) => {
  switch (action.type) {
    case "FOO_PENDING": {
      return {...state, fetching: true};
    }
    case "FOO_REJECTED": {
      return {...state, fetching: false, error: action.payload};
    }
    case "FOO_FULFILLED": {
      return {
        ...state,
        fetching: false,
        fetched: true,
        data: action.payload,
      };
    }
  }
  return state;
};

1
使用redux-promises,你可以像这样处理问题,我认为这是一种优雅的方式。
首先,在redux状态中设置一个属性,用于保存可能发生的任何ajax错误。
ajaxError: {},

第二步,设置一个 reducer 来处理 AJAX 错误:

export default function ajaxErrorsReducer(state = initialState.ajaxError, action) {
  if (action.error) {
    const { response } = action.payload;
    return {
      status: response.status,
      statusText: response.statusText,
      message: response.data.message,
      stack: response.data.stack,
    };
  }
  return state;
}

最后,创建一个非常简单的React组件,如果有任何错误,它将呈现错误(我正在使用react-s-alert库显示漂亮的警报):
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import Alert from 'react-s-alert';

class AjaxErrorsHandler extends Component {

  constructor(props, context) {
    super(props, context);
    this.STATUS_GATE_WAY_TIMEOUT = 504;
    this.STATUS_SERVICE_UNAVAILABLE = 503;
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.ajaxError !== nextProps.ajaxError) {
      this.showErrors(nextProps.ajaxError);
    }
  }

  showErrors(ajaxError) {
    if (!ajaxError.status) {
      return;
    }
    Alert.error(this.getErrorComponent(ajaxError), {
      position: 'top-right',
      effect: 'jelly',
      timeout: 'none',
    });
  }

  getErrorComponent(ajaxError) {
    let customMessage;
    if (
      ajaxError.status === this.STATUS_GATE_WAY_TIMEOUT ||
      ajaxError.status === this.STATUS_SERVICE_UNAVAILABLE
    ) {
      customMessage = 'The server is unavailable. It will be restored very shortly';
    }
    return (
      <div>
        <h3>{ajaxError.statusText}</h3>
        <h5>{customMessage ? customMessage : ajaxError.message}</h5>
      </div>
    );
  }

  render() {
    return (
      <div />
    );
  }

}

AjaxErrorsHandler.defaultProps = {
  ajaxError: {},
};

AjaxErrorsHandler.propTypes = {
  ajaxError: PropTypes.object.isRequired,
};

function mapStateToProps(reduxState) {
  return {
    ajaxError: reduxState.ajaxError,
  };
}

export default connect(mapStateToProps, null)(AjaxErrorsHandler);

您可以在您的App组件中包含此组件。


0
这可能不是最好的方法,但对我来说有效。通过将我的组件的“this”作为var context传递。然后当我收到响应时,只需执行在我的组件上下文中定义的方法即可。在我的组件中,我有 successHdl 和 errorHdl 方法。从这里,我可以像平常一样触发更多的redux操作。我检查了所有先前的答案,似乎对于如此琐碎的任务过于艰巨。
export function updateJob(payload, context){

    const request = axios.put(UPDATE_SOMETHING, payload).then(function (response) {
        context.successHdl(response);
    })
    .catch(function (error) {
        context.errorHdl(error);
    });;

    return {
        type: UPDATE_SOMETHING,
        payload: payload,
    }
}

-1

不要使用redux-promise。它会使本来非常简单的事情变得过于复杂。

相反,阅读redux文档:http://redux.js.org/docs/advanced/AsyncActions.html

这将让你更好地理解如何处理这种交互,并学习如何自己编写比redux-promise更好的代码。


5
这并不算是一个答案。它是主观的,更糟糕的是,没有提供任何支持您观点的信息。也许你说得没错,但如果没有详细信息,那么这只是一个没有实际行动意义的简短抱怨。 - Dave Newton
1
这是一条建议,我给出了充分的理由。被采纳的答案也没有使用redux-promise。我可能应该将其作为评论添加到问题中,而不是作为答案。这是真的。 - SpoBo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接