我可能遗漏了一些非常明显的东西,想要清除自己。
这是我的理解。
在一个天真的反应组件中,我们有states
& props
。使用state
更新setState
会重新呈现整个组件。 props
大部分都是只读的,更新它们并不合理。
在通过类似store.subscribe(render)
之类的订阅redux商店的react组件中,显然每次更新商店时都会重新呈现。
react-redux有一个帮助器connect()
,它将部分状态树(组件感兴趣)和actionCreators注入props
组件,通常是通过类似的东西
const TodoListComponent = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
但是要理解setState
对于TodoListComponent
对还原状态树更改(重新渲染)做出反应至关重要,我无法找到任何state
或setState
组件文件中的TodoList
相关代码。它的内容如下:
const TodoList = ({ todos, onTodoClick }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => onTodoClick(todo.id)}
/>
)}
</ul>
)
有人能指出我正确的方向,我错过了什么吗?
P.S我跟随与redux包(https://github.com/reactjs/redux/tree/master/examples/todos/src/containers)捆绑在一起的待办事项列表示例。
答案 0 :(得分:70)
connect
函数生成一个订阅商店的包装器组件。调度操作时,将通知包装器组件的回调。然后,它会运行您的mapState
函数,并且浅析此时间的结果对象与上次的结果对象相比(因此,如果您要重写 a redux存储字段具有相同的值,它不会触发重新渲染)。如果结果不同,那么它会将结果传递给您的“真实”组件“作为道具。
丹·阿布拉莫夫在https://gist.github.com/gaearon/1d19088790e70ac32ea636c025ba424e写了一个很好的简化版connect
来说明基本思想,虽然它没有显示任何优化工作。我还链接到Redux performance上的一些文章,讨论了一些相关的想法。
<强>更新强>
React-Redux v6.0.0对连接组件如何从商店接收数据做了一些重大的内部更改。
作为其中的一部分,我写了一篇帖子,解释connect
API及其内部工作原理以及它们如何随时间变化:
<强> Idiomatic Redux: The History and Implementation of React-Redux 强>
答案 1 :(得分:7)
我的答案有点偏离左侧领域。它揭示了导致我发表这篇文章的问题。在我的情况下,即使它收到了新的道具,它似乎也没有重新渲染 React开发人员对这个经常被问到的问题有一个答案,如果(商店)发生了变异,那么99%的时间是反应的原因不会重新呈现。 但其他1%没什么。这里并非如此突变。
TLDR;
componentWillReceiveProps
是state
与新props
同步的方式。
边缘案例:state
更新后,然后应用 重新呈现!
事实证明,如果您的应用仅使用state
来显示其元素,则props
可以更新,但state
不会更新,因此不会重新呈现。 强>
我的state
依赖于从redux props
收到的store
。我需要的数据还没有存储在商店中,所以我从componentDidMount
获取了它,这是正确的。当我的reducer更新存储时,我得到了道具,因为我的组件是通过mapStateToProps连接的。但页面没有渲染,状态仍然是空字符串。
这方面的一个例子是用户从保存的URL加载了“编辑帖子”页面。您可以从网址访问postId
,但信息不在store
中,因此您可以获取它。页面上的项目是受控制的组件 - 因此您显示的所有数据都在state
。
使用redux,获取数据,更新存储,组件connect
,但应用程序未反映更改。仔细看,收到props
,但应用没有更新。 state
没有更新。
好吧,props
会更新并传播,但state
不会。
您需要明确指示state
更新。
您无法在render()
中执行此操作,componentDidMount
已完成周期。
componentWillReceiveProps
用于更新依赖于已更改state
值的prop
属性。
示例用法:
componentWillReceiveProps(nextProps){
if (this.props.post.category !== nextProps.post.category){
this.setState({
title: nextProps.post.title,
body: nextProps.post.body,
category: nextProps.post.category,
})
}
}
我必须对这篇文章大加赞赏,这篇文章让我了解了许多其他帖子,博客和回购未能提及的解决方案。其他任何人在找到这个明显模糊的问题的答案时遇到了困难,这就是:
ReactJs component lifecycle methods — A deep dive
componentWillReceiveProps
是您更新state
以与props
更新保持同步的地方。
state
更新后,然后字段取决于state
执行重新呈现!
答案 2 :(得分:0)
我只知道redux会做的事情,如果您的组件依赖于突变状态,那么在商店状态改变时会调用componentWillRecieveProps,然后您应该强制组件进行更新 就是这样
1-store State change-2-call(componentWillRecieveProps(()=> {3-component state change}))
答案 3 :(得分:0)
此答案是布莱恩·沃恩(Brian Vaughn)题为You Probably Don't Need Derived State的文章的摘要(2018年6月7日)。
从道具派生状态是所有形式的反模式。包括使用较旧的componentWillReceiveProps
和较新的getDerivedStateFromProps
。
请考虑以下解决方案,而不是从props派生状态。
两个最佳做法建议
建议1.完全受控的组件function EmailInput(props) {
return <input onChange={props.onChange} value={props.email} />;
}
建议2.完全不受控制的组件,带钥匙
// parent class
class EmailInput extends Component {
state = { email: this.props.defaultEmail };
handleChange = event => {
this.setState({ email: event.target.value });
};
render() {
return <input onChange={this.handleChange} value={this.state.email} />;
}
}
// child instance
<EmailInput
defaultEmail={this.props.user.email}
key={this.props.user.id}
/>
如果由于某种原因建议不适合您的情况,则有两种选择。
备选方案1:使用ID属性重置不受控制的组件class EmailInput extends Component {
state = {
email: this.props.defaultEmail,
prevPropsUserID: this.props.userID
};
static getDerivedStateFromProps(props, state) {
// Any time the current user changes,
// Reset any parts of state that are tied to that user.
// In this simple example, that's just the email.
if (props.userID !== state.prevPropsUserID) {
return {
prevPropsUserID: props.userID,
email: props.defaultEmail
};
}
return null;
}
// ...
}
备选方案2:使用实例方法重置不受控制的组件
class EmailInput extends Component {
state = {
email: this.props.defaultEmail
};
resetEmailForNewUser(newEmail) {
this.setState({ email: newEmail });
}
// ...
}