youngwind / blog

梁少峰的个人博客
4.66k stars 385 forks source link

如何在redux中捕获处理ajax请求错误 #67

Open youngwind opened 8 years ago

youngwind commented 8 years ago

问题

以前用react+redux开发项目的时候,很少考虑到异步获取数据失败之后应该怎么办的问题。这次趁着做项目,在这方面做了一些探索。

以前的做法

//action.js
exports.requestPractice = function (practiceId) {
  return {
    type: ActionTypes.REQUEST_PRACTICE,
    practiceId
  };
};

exports.receivedPractice = function (practiceId, res) {
  return {
    type: ActionTypes.RECEIVE_PRACTICE,
    practiceId: practiceId,
    practice: res.data
  };
};

exports.fetchPractice = function (practiceId) {
  return function (dispatch) {
    dispatch(exports.requestPractice(practiceId));
    return fetch(`${Config.api}/practice?id=${practiceId}`, {
      credentials: 'same-origin'
    }).then(res => res.json())
      .then(json => dispatch(exports.receivedPractice(practiceId, json)));
  };
};
// 在container进行调用
componentDidMount:function(){
 // 直接进行请求
 fetchPractice(practiceId);
}

这样子简单粗暴的方法主要欠缺考虑下面两种情况:

  1. 网络错误导致请求失败
  2. 请求参数错误或者后端返回code状态码不为0

    解决思路

  3. 在每个ajax请求的地方添加http状态码和code码的校验,如果不正确,则抛出错误。
  4. 在调用action方法的地方捕获处理错误
  5. 添加数据请求失败action,置fetchFail标志位为true,以表示上一次的数据请求失败
  6. 添加错误信息action和reducer,以存储错误提示,编写相应的统一错误提示component以显示错误提示。

    现在的代码

// 请求部分添加校验
exports.fetchPractice = function (practiceId) {
  return function (dispatch) {
    dispatch(exports.requestPractice(practiceId));
    return fetch(`${Config.api}/practice?id=${practiceId}`, {
      credentials: 'same-origin'
    }).then(Utils.checkStatus)
      .then(res => res.json())
      .then(Utils.checkCode)
      .then(json => dispatch(exports.receivedPractice(practiceId, json)));
  };
};
/**
 * 检查fetch请求返回是否报错
 * @param response
 */
exports.checkStatus = function (response) {
  let {status, statusText} = response;

  if (status >= 200 && status < 300) {
    return response;
  }

  let error = new Error();
  error = {
    status,
    msg: statusText
  };
  throw error;

};
/**
 * 检查code状态码
 * @param json
 */
exports.checkCode = function (json) {
  let {code, msg} = json;

  if (json.code === 0) {
    return json;
  }

  let error = new Error();
  error = {
    status: 200,
    code,
    msg
  };
  throw error;

};
// 调用action的时候捕获错误
// 将捕获到的错误通过error action设置到store的error字段中
// 从而引发错误提示component的渲染显示
fetchPractice(practiceId).catch(err => {
        fetchPracticeFail();
        setErrorTip(err);
      });

遗留问题

如果每次ajax数据请求错误都有统一的处理方式,是不是可以考虑将错误的捕获直接放在action里面呢?而不是每次调用的时候再捕获处理错误。但是这样又容易造成耦合,这个问题还需要思考实践一段时间。

jun-lu commented 7 years ago

nice ,

fetchPracticeFail这个函数用来处理什么

用try catch是否也行

try{
   fetchPractice(practiceId)
}catch(e){
  setErrorTip(err);
}
ian4hu commented 7 years ago

可以每次发送action去请求数据时附带一个error的action作为payload,当出现错误时,dispatch这个action

dispatch(fetchData(params, fetchDataError('网络错误,无法读取'))