我在改变状态吗?如果可以,有哪些解决方案?
Am i mutating state? If so what solutions are available?
我有一个简单的组件,它从对象数组构建报告列表,我正在尝试添加过滤和排序功能(在一定程度上确实有效),排序功能有效,但我尽管试图将原始状态复制到新数组中,但我担心我正在改变状态。
我的过滤器第一次工作,但之后它不会过滤其他结果,因为状态已发生变化,或者因为我无法过滤初始状态?这让我困惑了几个小时,非常感谢任何帮助。
非常感谢
constructor(props) {
super(props);
this.state = {
reports: props.data
};
this.handleSortBy = this.handleSortBy.bind(this);
this.handleFilterType = this.handleFilterType.bind(this);
}
handleSortBy(event) {
const copy = [...this.state.reports];
if (event.target.value === 'A-Z') {
return this.setState({
reports: copy.sort((a, b) => a.name.localeCompare(b.name))
});
}
if (event.target.value === 'Z-A') {
this.setState({
reports: copy
.sort((a, b) => a.name.localeCompare(b.name))
.reverse()
});
}
}
handleFilterType(event) {
this.setState({
reports: this.state.reports.filter(item => {
return item.type === event.target.value;
})
});
}
提前致谢:)
我认为问题是每次过滤时,您都会从 state.reports
中删除项目,并且永远不会从 props.data
中取回它们。
相反,您应该在每次 filter/sort.
时从 props.data
重建 state.reports
或者更好的方法,IMO,将 filtering/sorting 设置存储在 state
中,并在 render
中执行 props.data
的 filtering/sorting基于当前设置。
根据您的用例,一种简单的方法是在您的两个更新函数中将 this.state.reports
替换为 this.props.data
。 (虽然,如果你这样做,你不能同时应用排序和过滤)
但我同意 ModestLeech 的观点,即最好将过滤器存储在状态中。为此,请将您的过滤器代码更改为:
handleFilterType(event) {
this.setState({
filter: event.target.value;
})
});
}
并且在您的渲染方法中,假设您使用的是地图,您可以将地图更改为 this.state.reports.filter(item => (this.state.filter===undefined || item.type===this.state.filter)).map(...)
。
编辑:
使用 React,您需要担心两种类型的突变。
一个是不改变你的道具(即改变你的组件无法控制的状态)。由于 Javascript 通过引用传递数组和对象,如果您在 this.props.data
上 sort
它实际上会修改存在于组件的父级(或祖父级,或定义它的任何地方)中的数组.好消息是您的原始代码已经避免了这种情况 - 可能是无意的。 handleSortBy
在改变它之前复制了数组,handleFilterType
使用 Array.prototype.filter
,它创建了一个新数组。
您需要担心的第二个突变是在不使用 setState
的情况下更改本地状态。在 render
中调用 this.state.reports.sort(...)
会改变本地状态,但 React 不会知道也不会重新渲染。这不是问题,因为你在需要它之前就进行了排序,但是直接改变状态是危险的,因为它可能会导致错误,你作为开发人员认为在某些条件下应该或不应该改变某些东西但现实是不同的.
您最初的 filter
问题与意外突变无关。问题是第一次使用过滤器调用 this.setState
时,您覆盖了 this.state.reports
并且无法取回它。它还有一个问题,如果 this.props.data
在父级中发生变化,新的报告将永远不会出现在这个组件中,因为你只在第一次创建组件时读取道具。
推荐的 React 方法是不要存储来自 prop 的状态中的东西,除非你只是使用 prop 来设置一些初始状态,并且所有未来的更新都将来自组件内部。但是假设这个组件最终并不拥有报告列表,那么在 运行 中复制 render
中的 this.props.data
并对其应用您的排序和过滤器会更简单。然后,如果在组件树的更高层添加或删除任何内容,您将不必编写任何特殊逻辑来更新该组件的本地状态。
我有一个简单的组件,它从对象数组构建报告列表,我正在尝试添加过滤和排序功能(在一定程度上确实有效),排序功能有效,但我尽管试图将原始状态复制到新数组中,但我担心我正在改变状态。
我的过滤器第一次工作,但之后它不会过滤其他结果,因为状态已发生变化,或者因为我无法过滤初始状态?这让我困惑了几个小时,非常感谢任何帮助。
非常感谢
constructor(props) {
super(props);
this.state = {
reports: props.data
};
this.handleSortBy = this.handleSortBy.bind(this);
this.handleFilterType = this.handleFilterType.bind(this);
}
handleSortBy(event) {
const copy = [...this.state.reports];
if (event.target.value === 'A-Z') {
return this.setState({
reports: copy.sort((a, b) => a.name.localeCompare(b.name))
});
}
if (event.target.value === 'Z-A') {
this.setState({
reports: copy
.sort((a, b) => a.name.localeCompare(b.name))
.reverse()
});
}
}
handleFilterType(event) {
this.setState({
reports: this.state.reports.filter(item => {
return item.type === event.target.value;
})
});
}
提前致谢:)
我认为问题是每次过滤时,您都会从 state.reports
中删除项目,并且永远不会从 props.data
中取回它们。
相反,您应该在每次 filter/sort.
时从props.data
重建 state.reports
或者更好的方法,IMO,将 filtering/sorting 设置存储在 state
中,并在 render
中执行 props.data
的 filtering/sorting基于当前设置。
根据您的用例,一种简单的方法是在您的两个更新函数中将 this.state.reports
替换为 this.props.data
。 (虽然,如果你这样做,你不能同时应用排序和过滤)
但我同意 ModestLeech 的观点,即最好将过滤器存储在状态中。为此,请将您的过滤器代码更改为:
handleFilterType(event) {
this.setState({
filter: event.target.value;
})
});
}
并且在您的渲染方法中,假设您使用的是地图,您可以将地图更改为 this.state.reports.filter(item => (this.state.filter===undefined || item.type===this.state.filter)).map(...)
。
编辑:
使用 React,您需要担心两种类型的突变。
一个是不改变你的道具(即改变你的组件无法控制的状态)。由于 Javascript 通过引用传递数组和对象,如果您在 this.props.data
上 sort
它实际上会修改存在于组件的父级(或祖父级,或定义它的任何地方)中的数组.好消息是您的原始代码已经避免了这种情况 - 可能是无意的。 handleSortBy
在改变它之前复制了数组,handleFilterType
使用 Array.prototype.filter
,它创建了一个新数组。
您需要担心的第二个突变是在不使用 setState
的情况下更改本地状态。在 render
中调用 this.state.reports.sort(...)
会改变本地状态,但 React 不会知道也不会重新渲染。这不是问题,因为你在需要它之前就进行了排序,但是直接改变状态是危险的,因为它可能会导致错误,你作为开发人员认为在某些条件下应该或不应该改变某些东西但现实是不同的.
您最初的 filter
问题与意外突变无关。问题是第一次使用过滤器调用 this.setState
时,您覆盖了 this.state.reports
并且无法取回它。它还有一个问题,如果 this.props.data
在父级中发生变化,新的报告将永远不会出现在这个组件中,因为你只在第一次创建组件时读取道具。
推荐的 React 方法是不要存储来自 prop 的状态中的东西,除非你只是使用 prop 来设置一些初始状态,并且所有未来的更新都将来自组件内部。但是假设这个组件最终并不拥有报告列表,那么在 运行 中复制 render
中的 this.props.data
并对其应用您的排序和过滤器会更简单。然后,如果在组件树的更高层添加或删除任何内容,您将不必编写任何特殊逻辑来更新该组件的本地状态。