如何在将数组推入对象时避免突变
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
如果你使用的是 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;
};
我有一个持有公司的对象,在公司内部,有一组团队。
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
如果你使用的是 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;
};