首页 文章

使用React中的Redux和Redux Thunk处理不成功的Ajax调用

提问于
浏览
0

对于我的React组件,我使用Redux(react-redux)和Redux Thunk来维护应用程序状态 .

有一个 ShowGroup 组件从后端检索 group . 如果该组不存在(或在任何其他错误情况下),则调用错误回调以更新状态 .

class ShowGroup extends Component {
  constructor(props) {
    super(props);
    this.groupId = this.props.match.params.id;
    this.state = {
      'loaded': false,
      'error': null
    };
  }

  _fetchGroupErrorCallback = response => {
    this.setState({loaded: true, error: response});
  }

  componentDidMount() {
    this.props.fetchGroup(this.groupId, this._fetchGroupErrorCallback);
  }

  render() {
    //what is the best practice here ?
    let group = this.props.groups[this.groupId];
    if (!group) {
      if (!this.state.loaded) {
        return <div>Loading group ....</div>;
      }
      if (this.state.error) {
        return <div>{this.state.error.data.message}</div>;
      }
    }
    return (
      <div className="show-group">
        <form>
          {_.map(FIELDS, renderField.bind(this))}
        </form>
      </div>
    );
  }
}

function mapStateToProps(state) {
  return { groups: state.groups };
}

const mapDispatchToProps = (dispatch) => {
    return {
        fetchGroup: (id, callback) => dispatch(fetchGroup(id, callback)),
        updateGroup: (id) => dispatch(updateGroup(id))
    };
};

export default reduxForm({
  validate,
  //a unique id for this form
  form:'ShowGroup',
  fields: _.keys(FIELDS),
  fields_def: FIELDS
})(
  connect(mapStateToProps, mapDispatchToProps)(ShowGroup)
);

(这是一个无关紧要的 redux-form 组件)

export function fetchGroup(id, fetchErrorCallback) {
  return (dispatch) => {
    axios.get(URL, AUTHORIZATION_HEADER)
      .then(response => {
        dispatch(groupFetched(response))
      })
      .catch(({response}) => {
        fetchErrorCallback(response);
        dispatch(groupFetchErrored(response));
      })
  };
}

groupFetchedErrored 动作创作者:

export function groupFetchErrored(response) {
  //handle the error here
  return {
     type: FETCH_GROUP_ERROR,
     response
  }
}

还原剂:

export default function(state=INITIAL_STATE, action) {
  switch(action.type) {
    //....
    case FETCH_GROUP_ERROR:
      return _.omit(state, action.response.data.id);
    default:
      return state;
  }
}

The problem:

A. 如果出现错误响应,组件将被渲染三次:1 . 首次加载(在进行ajax调用之后)2 . 调用 _fetchGroupErrorCallback 并设置状态,这将导致渲染3. groupFetchErrored 是调度导致另一个渲染

B. 如果响应成功,组件会被渲染两次,但状态不正确,因为没有更新它(并且我没有't think it'正确为它添加回调)

在React中使用Redux和Redux-Thunk时,处理ajax错误响应的最佳实践是什么?动作创建者和减速器应该如何通知组件有什么问题?

Update 1

这是我的根减速器:

const rootReducer = combineReducers({
  form: formReducer,
  groups: groupReducer
});

export default rootReducer;

如你所见,我有 groups 这是一个形式的对象:

{
   groupId1: groupData1,
   groupId2, groupData2, 
   ....
}

所以,另一个问题是,我应该如何指定一个组正在加载(我在 groups 级别上没有 isLoadingerrorFetching ,并且它没有't seem to be a good idea to add one entry for each invalid group id user may try. Or maybe there'方式将无效的组ID放入 Map 然后清理它?我不知道但是,我知道应该在哪里发生 .

1 回答

  • 1

    将与获取相关的所有状态数据放入Redux存储中 . 然后商店成为你的唯一真相来源 .

    然后,您可以完全省略 fetchErrorCallback .

    您已经在减速器中有一个地方通知错误:

    case FETCH_GROUP_ERROR:
      return _.omit(state, action.response.data.id);
    

    您可以将错误存储在redux状态:

    case FETCH_GROUP_ERROR:
      const stateWithoutId = _.omit(state, action.response.data.id);
      return {
        ...stateWithoutId,
        errorFetching: action.response.error,
        isLoading: false
      }
    

    然后通过 mapStateToPropserrorFetchingisLoading 传递给您的组件,就像您已经使用 groups 一样 .

    同样要正确跟踪 isLoading ,最好在开始提取时(在调用 axios.get 之前)调度另一个动作,也许 FETCH_GROUP_STARTED . 然后在您的reducer中,当调度该动作类型时,将 isLoading 设置为 true .

    case GROUP_FETCHED: 
      return { 
        ...state, 
        isLoading: false,
        group: action.response.data // or however you're doing this now.
    
      };
    case FETCH_GROUP_STARTED:
      return { ...state, isLoading: true }; // or maybe isFetchingGroups is a better name
    

    如果这是我的代码,我会做出另一个更改:我不会将 response 传递给动作创建者,而是删除相关数据并将其传递给他们 . 因此reducer不需要知道如何处理 response 对象;它只是接收正确显示组件所需的数据 . 这样,处理axios调用并且必须知道它们返回的对象的形状的所有代码都在一个地方 - 动作函数 . 就像是:

    动作创作者

    export function groupFetchErrored(errorWithId) {
      return {
         type: FETCH_GROUP_ERROR,
         payload: errorWithId
      }
    }
    
    export function groupFetchSucceeded(groupData) 
      return {
         type: GROUP_FETCHED,
         payload: groupData
      }
    }
    

    行动

    export function fetchGroup(id, fetchErrorCallback) {
      return (dispatch) => {
        axios.get(URL, AUTHORIZATION_HEADER)
          .then(response => {
            dispatch(groupFetched(response.data)) // or whatever chunk you need out of response
          })
          .catch(({response}) => {
            dispatch(groupFetchErrored({
                error: response.error, 
                id: response.data.id
              }
            );
          })
      };
    }
    

    减速机

    你可能已经注意到我在上面的动作创建者中使用了 action.payload .

    它需要与动作创建者紧密耦合;它知道数据总是在 payload 字段中 . (也许这就是你用 response 做的事情,但看起来它似乎专门用于http响应 . )

    case GROUP_FETCHED: 
      return { 
        ...state, 
        isLoading: false,
        group: action.payload
      };
    case FETCH_GROUP_STARTED:
      return { ...state, isLoading: true }; // no payload needed
    case FETCH_GROUP_ERROR:
      const dataId = action.payload.id;
      const stateWithoutId = _.omit(state, dataId);
      return {
        ...stateWithoutId,
        errorFetching: action.payload.error,
        isLoading: false
      }
    

相关问题