如何在将数组推入对象时避免突变

How to avoid mutations while pushing an array inside an object

我有一个持有公司的对象,在公司内部,有一组团队。

  company: {
    teams: [
      {
        name: 'test team',
        description: 'dddd',
        team_manager: null,
        company: '592577d5b591966c8e535865',
        permalink: 'test-team',
        createdAt: '2017-05-30T07:38:58.983Z',
        updatedAt: '2017-05-30T07:38:58.983Z',
        id: '592d219277923054118e7299'
      }
    ],
    name: 'test company2',
    createdAt: '2017-05-24T12:08:53.418Z',
    updatedAt: '2017-05-24T12:08:53.419Z',
    id: '592577d5b591966c8e535865'
  }
}

添加团队时,我正在使用此减速器将团队推送到数组。

case types.ADD_TEAM_SUCCESS :
  return Object.assign({}, state, state.teams.push(action.newTeam));

这工作正常,但是,在控制台中我收到一条警告:

'Error: A state mutation was detected inside a dispatch, in the path: company.teams.0'

关闭对象并将新团队推送到数组的正确方法是什么?

问题是 Object.assign() 没有对您的对象 [1] 进行深度克隆(参见 "Warning for Deep Clone")。

如果您的堆栈中没有任何实用程序库(如 lodash),您可以使用 JSON 编码和解码来完成工作:

case types.ADD_TEAM_SUCCESS:
    let stateCopy = JSON.parse(JSON.stringify(state));
    state.teams.push(action.newTeam);
    return state;

[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

See this link

如果你使用的是 es2015,我可以这样做:

return Object.assign({}, state, { teams: [...state.teams, action.newTeam] });

您正在将对象直接推送到 state.teams。您应该先制作一份副本,然后再使用它。如果您不能使用扩展运算符,则可以将 state.teams 切片为一个新变量,然后推送到该变量。

如前所述,Object.assign() 不会对您的对象进行深度克隆。

我的建议是将您的商店规范化并分成公司和团队两个商店。

比使用 combineReducers 并将其连接到一个存储中,您将简化 reducer 并使代码更具可读性。你也可以玩性能。

我的意思是...如果您的组件只与公司合作并且您的团队发生变化,您将避免轻易重新渲染。

查看 Dan Abramov 炉排视频教程 Redux: Normalizing the State Shape

我会以某种方式做到这一点(抱歉打字稿中的代码)

export interface ICompanyItem {    
    name: string;    
    id: string;
    createdAt: Date;
    updatedAt: Date;    
}

export interface ITeamItem {
    id: string;
    companyId: string
    name: string;
    description: string;
    team_manager: string;
    createdAt: Date;
    updatedAt: Date;
}

export interface ICompaniesState {
    items: { [id: string]: ICompanyItem };
}

export interface ITeamsState {
    items: { [id: string]: ITeamItem };
}


export const teamsReducer: Reducer<ITeamsState> = (state: ITeamsState, action: Action) => {

    if (isActionType(action, AddNewTeamItem)) {
        let itemsClone: { [id: string]: ITeamItem } = { ...state.items, [action.id]: action.team };

        return {
            ...state,
            items: itemsClone
        };
    }

    return state || unloadedState;
};