Redux 的 mapStateToProps 如何触发 React 组件更新
How Redux's mapStateToProps triggers a React component update
在我的 react/redux 项目中,我有一个对数组进行排序的操作:
case 'SORT_BY_NAME':
var newList = state.list; //.slice()
newList.sort(function(a,b){
if (a.name > b.name){
return 1;
} else if (a.name < b.name){
return -1;
} else {
return 0;
}
});
return Object.assign({}, state, { list: newList } );
该动作成功地根据所需字段对数组进行了排序,
状态对象的形状符合我的预期,并且在我的反应组件中 mapStateToProps 被击中,并且在 mapStateToProps 中,状态值准确地反映了数组的排序状态。
初始渲染很好。但是排序操作未能导致再次调用我的组件的渲染方法,因此 UI 中的数据从未出现排序。
我项目的架构类似于 Redux 文档中的 Reddit example。但是,我还使用 react-redux 与 mapStateToProps 的连接。下面是我的组件设计的精简版:
class MyComponent extends React.Component{
render(){
<ul>
{list.map(item =>
<li key={item.id}>
{item.name}
</li>
)}
</ul>
}
}
function mapStateToProps(state){
return {
list: state.list;
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(MyComponent)
最终我意识到解决我的问题的简单方法是在使用 slice() 对列表进行排序之前克隆列表。因此我的问题不是如何解决问题,而是为什么会出现这个问题。问题是 redux 的 mapStateToProps 没有调用 render() 吗?或者 react 组件没有看到新的状态对象?
通过在 mapStateToProps 中设置一个断点,看起来好像状态 set/changed 是正确的;根据 Chrome 调试器中的 Redux devtools,商店看起来像我认为的那样。
我的项目太大,无法创建完全重现我的问题的演示 fiddle。当我试图创建一个最小尺寸的 fiddle 来重现这个问题时,我更加困惑了,因为当我不使用 Redux 的连接时,这个问题不会发生。 Link to fiddle.
总而言之,我的问题是为什么在使用 mapStateToProps 时排序没有生效。我最好的猜测是,如果我的排序操作改变了以前的状态,使新状态与以前的状态相匹配,那么就没有需要重新渲染的变化。也许 mapStateToProps 会比较之前的状态和新的状态,但它不会将新的 props 与 DOM 中的 props 表示进行比较。
React Redux connect()
经过大量优化,并在 mapStateToProps
是纯函数的假设下运行。
通过调用 state.list.sort()
来改变存储状态在 Redux 中是不允许的。 通常最好记住哪些方法正在改变,哪些是不是。例如,splice()
是突变,而 slice()
不是; push()
正在变异,但 concat()
没有。你永远不应该对从商店获得的状态使用变异方法。
您也可以看看 redux-immutable-state-invariant to make these cases easier to detect, or use Immutable collections for JavaScript or seamless-immutable 之类的东西来强制执行不变性。这是可选的,由您决定。
在我的 react/redux 项目中,我有一个对数组进行排序的操作:
case 'SORT_BY_NAME':
var newList = state.list; //.slice()
newList.sort(function(a,b){
if (a.name > b.name){
return 1;
} else if (a.name < b.name){
return -1;
} else {
return 0;
}
});
return Object.assign({}, state, { list: newList } );
初始渲染很好。但是排序操作未能导致再次调用我的组件的渲染方法,因此 UI 中的数据从未出现排序。
我项目的架构类似于 Redux 文档中的 Reddit example。但是,我还使用 react-redux 与 mapStateToProps 的连接。下面是我的组件设计的精简版:
class MyComponent extends React.Component{
render(){
<ul>
{list.map(item =>
<li key={item.id}>
{item.name}
</li>
)}
</ul>
}
}
function mapStateToProps(state){
return {
list: state.list;
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(MyComponent)
通过在 mapStateToProps 中设置一个断点,看起来好像状态 set/changed 是正确的;根据 Chrome 调试器中的 Redux devtools,商店看起来像我认为的那样。
我的项目太大,无法创建完全重现我的问题的演示 fiddle。当我试图创建一个最小尺寸的 fiddle 来重现这个问题时,我更加困惑了,因为当我不使用 Redux 的连接时,这个问题不会发生。 Link to fiddle.
总而言之,我的问题是为什么在使用 mapStateToProps 时排序没有生效。我最好的猜测是,如果我的排序操作改变了以前的状态,使新状态与以前的状态相匹配,那么就没有需要重新渲染的变化。也许 mapStateToProps 会比较之前的状态和新的状态,但它不会将新的 props 与 DOM 中的 props 表示进行比较。
React Redux connect()
经过大量优化,并在 mapStateToProps
是纯函数的假设下运行。
通过调用 state.list.sort()
来改变存储状态在 Redux 中是不允许的。 通常最好记住哪些方法正在改变,哪些是不是。例如,splice()
是突变,而 slice()
不是; push()
正在变异,但 concat()
没有。你永远不应该对从商店获得的状态使用变异方法。
您也可以看看 redux-immutable-state-invariant to make these cases easier to detect, or use Immutable collections for JavaScript or seamless-immutable 之类的东西来强制执行不变性。这是可选的,由您决定。