处理react-redux应用程序中的请求调用响应以避免使用回调

时间:2018-02-06 22:40:57

标签: reactjs redux

这是一个设计问题,但我想知道是否有最好的做法来处理这种情况。

我有一个react redux应用程序。 app状态结构如下所示:

{
   groups: {gid1: group1, gid2: group2, ....},
   expenses: {eid1: expense1, eid2: expense2, ...}, 
   //.... the rest
}

NewGroup组件旨在创建一个新组。如果创建请求(对后端的ajax请求)成功,则会将其添加到应用程序状态的组列表中。作为模态的NewGroup已关闭,组列表将更新。

如果请求未成功,则不应关闭NewGroup组件。

主要问题是:我是否应该在应用程序状态中合并请求状态(如果成功或不成功,后一种情况下的错误消息,如果结果是否返回等)?这是我真正怀疑的地方,因为它使应用程序状态非常丑陋,大而且在某些情况下它无法处理某些情况,在这种情况下:创建一个新组。

假设我将响应状态代码和错误消息添加到应用程序状态:

{
   groups: {
      newGroupId1: {
          data: newGroupData1,// null in case it was not successful
          isLoading: true/false, // if the call is returned or not
          errorFetching: message1
      }
   }
}

在对后端的第二个不成功的创建请求中,无法区分先前和当前的呼叫,因为它们都具有errorFetching和类似的isLoading值。 (例如,errorFetchingisLoading都有“找不到群组”和“{1}}

在使用redux时,区分请求调用的状态和应用状态中的实际数据的最佳做法是什么?

更新1

这是减速器:

import { FETCH_GROUPS_SUCCESS, FETCH_GROUPS_ERROR, CREATE_GROUP_SUCCESS, CREATE_GROUP_ERROR, DELETE_GROUP, FETCH_GROUP_SUCCESS, FETCH_GROUP_ERROR } from '../actions/creators';

export const FULL_GROUP = 'full_group';
export const SNIPPET_GROUP = 'snippet_group';

const INITIAL_STATE = {
  data: null,
  isLoading: true,
  errorFetching: null
};

export default function(state=INITIAL_STATE, action) {
  switch(action.type) {
    case FETCH_GROUPS_SUCCESS:
      return {
        data: _.zipObject(_.map(action.payload, group => group.id),
          _.map(action.payload, group => ({
              data: group,
              isLoading: true,
              errorFetching: null,
              mode: SNIPPET_GROUP
          }))
        ),
        isLoading: false,
        errorFetching: null
      };
    case FETCH_GROUPS_ERROR:
      return {
        data: null,
        isLoading: false,
        errorFetching: action.payload.error
      };
    case FETCH_GROUP_SUCCESS:
      return {
        data: {...state.data, [action.payload.id]: {
          data: action.payload,
          isLoading: false,
          errorFetching: null,
          mode: FULL_GROUP
        }},
        isLoading: false,
        errorFetching: null
      };
    case FETCH_GROUP_ERROR:
      const stateWithoutId = _.omit(state, action.payload.id);
      return {
        data: {...stateWithoutId},
        isLoading: false,
        errorFetching: null
      };
    case CREATE_GROUP_SUCCESS:
      debugger;
      //TODO: how to let the NewGroup know that a group was created successfuly ?
      return { ...state, [action.payload.id]: {
        data: action.payload,
        isLoading: true,
        errorFetching: null,
        mode: SNIPPET_GROUP
      }};
    default:
      return state;
  }
}

这是动作创建者:

export function groupsFetchSucceeded(response) {
  return {
     type: FETCH_GROUPS_SUCCESS,
     payload: response
  }
}

export function groupsFetchErrored(errorWithId) {
  return {
     type: FETCH_GROUPS_ERROR,
     payload: errorWithId
  }
}

export function groupCreateSucceeded(group) {
  return {
    type: CREATE_GROUP_SUCCESS,
    payload: group
  }
}

export function groupCreateErrored(errorWithId) {
  return {
    type: CREATE_GROUP_ERROR,
    payload: response
  }
}

export function groupFetchSucceeded(groupData) {
  return {
     type: FETCH_GROUP_SUCCESS,
     payload: groupData
  }
}

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

这是组件。请!无视这是一种redux形式,我不在乎它是一种还原形式,我正在寻找一个大概的想法。您可以假设它是react-redux组件mapStateToPropsmapDispatchToProps以及其他相关内容。

import { createGroup } from '../../actions';
import { validateName, validateDescription } from '../../helpers/group_utils';
import { renderField, validate } from '../../helpers/form_utils';

const validators = {
  name: validateName,
  description: validateDescription
};

class NewGroup extends Component {

  _onSubmit(values) {
    this.props.createGroup(values);
  }

  render() {
    const { handleSubmit } = this.props;
    return (
      <div>
        <form onSubmit={handleSubmit(this._onSubmit.bind(this))}>
          <Field name="name" label="Name" type="text" fieldType="input" component={renderField.bind(this)}/>
          <Field name="description" label="Description" type="text" fieldType="input" component={renderField.bind(this)}/>
          <button type="submit" className="btn btn-primary">Create</button>
          <button type="button" className="btn btn-primary" onClick={() => this.props.history.push('/group')}>Cancel</button>
        </form>
      </div>
    );
  }
}

export default reduxForm({
  validate,
  //a unique id for this form
  form:'NewGroup',
  validators
})(
  connect(null, { createGroup })(NewGroup)
);

1 个答案:

答案 0 :(得分:1)

你问题的棘手部分是,虽然你想在redux的状态下记录所有请求状态,但是如果没有得到很好的处理,请求的数量可能呈指数增长并占用很多州的数量。另一方面,我不认为区分请求数据和状态数据是一个大问题:你只需要将它们分开状态:

{
    groups: {gid1: group1, gid2: group2, ....},
    groupRequests: {grid1: request1, grid2: request2, ...},
    expenses: {eid1: expense1, eid2: expense2, ...},
    ...
}

每个组数据包含:

{
    data: groupData,
    lastRequestId: grid // optional
}

并且每个组请求数据包含:

{
    groupId: gid,
    isLoading: true/false,
    errorFetching: message
}

发送新请求时,您会在groupRequests中更新并在那里进行跟踪; NewGroup组件只需要持有groupId来查找最近发出的请求并查看其状态。当您需要知道先前请求的状态时,请从列表中获取它们。

同样,这种方法的问题在于groupRequests中的大部分数据都是一次性使用的,这意味着在某些时候(例如当NewGroup组件关闭时),你就不会这样做。甚至还需要它。因此,请确保在groupRequest变得过大之前正确地抛弃旧的请求状态。

我的建议是,在处理redux状态时,请尝试将其正常化。如果您需要1-n关系(组数据和创建组的请求数据),请将其进一步分解,而不是将它们组合在一起。并避免尽可能多地存储一次性使用的数据。