redux 连接的组件如何知道何时重新渲染?
Posted
技术标签:
【中文标题】redux 连接的组件如何知道何时重新渲染?【英文标题】:How does a redux connected component know when to re-render? 【发布时间】:2017-03-16 03:07:41 【问题描述】:我可能遗漏了一些非常明显的东西,并想澄清自己。
这是我的理解。
在一个幼稚的反应组件中,我们有states
和props
。用setState
更新state
会重新渲染整个组件。 props
大多是只读的,更新它们没有意义。
在通过store.subscribe(render)
之类的方式订阅redux store 的react 组件中,它显然会在每次更新store 时重新渲染。
react-redux 有一个帮助器 connect()
将状态树的一部分(组件感兴趣)和 actionCreators 作为 props
注入到组件中,通常通过类似
const TodoListComponent = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
但是了解setState
对于TodoListComponent
对redux状态树更改(重新渲染)做出反应是必不可少的,我在@中找不到任何state
或setState
相关代码987654336@ 组件文件。它的内容如下:
const TodoList = ( todos, onTodoClick ) => (
<ul>
todos.map(todo =>
<Todo
key=todo.id
...todo
onClick=() => onTodoClick(todo.id)
/>
)
</ul>
)
有人可以指出我缺少什么的正确方向吗?
P.S 我正在关注与redux package 捆绑在一起的待办事项列表示例。
【问题讨论】:
【参考方案1】:connect
函数生成一个订阅商店的包装器组件。当一个动作被调度时,包装组件的回调会被通知。然后它运行你的 mapState
函数,shallow-compares 这次的结果对象与上次的结果对象(所以如果你要重写 redux store具有相同值的字段,它不会触发重新渲染)。如果结果不同,则它将结果作为道具传递给您的“真实”组件。
Dan Abramov 在 (connect.js) 写了一个很棒的 connect
简化版本,说明了基本思想,尽管它没有显示任何优化工作。我还有一些关于Redux performance 的文章的链接,这些文章讨论了一些相关的想法。
更新
React-Redux v6.0.0 对连接组件从存储区接收数据的方式进行了一些重大的内部更改。
作为其中的一部分,我写了一篇文章,解释了connect
API 及其内部的工作原理,以及它们如何随着时间的推移而发生变化:
Idiomatic Redux: The History and Implementation of React-Redux
【讨论】:
谢谢!您是前几天通过有用链接帮助我@HN - news.ycombinator.com/item?id=12307621 的同一个人吗?很高兴认识你:) 是的,就是我 :) 我花了太多时间在网上寻找有关 Redux 的讨论 - Reddit、HN、SO、Medium 和其他各种地方。乐意效劳! (仅供参考,我强烈推荐 Discord 上的 Reactiflux 聊天频道。很多人在那里闲逛并回答问题。我通常在美国东部标准时间晚上在那里在线。邀请链接位于 reactiflux.com。) 假设这回答了问题,介意接受答案吗? :) 是比较对象本身还是比较前后对象的属性?如果是后者,是不是只检查第一级,是不是你说的“浅”? 是的,“浅相等检查”意味着比较每个对象的第一级字段:prev.a === current.a && prev.b === current.b && .....
。这假设任何数据更改都会导致新的引用,这意味着需要不可变的数据更新而不是直接突变。【参考方案2】:
我的回答有点离题。它揭示了导致我写这篇文章的一个问题。在我的情况下,应用似乎没有重新渲染,即使它收到了新的道具。 对于这个经常被问到的问题,React 开发人员有一个答案,如果 (store) 发生了变异,99% 的时间这就是 react 不会重新渲染的原因。 然而,剩下的 1% 却一无所获。突变不是这里的情况。
TLDR;
componentWillReceiveProps
是state
与新的props
保持同步的方式。
极端情况:一旦state
更新,然后应用会重新渲染!
事实证明,如果您的应用仅使用state
来显示其元素,props
可以更新,但state
不会,因此无需重新渲染。
我有 state
依赖于从 redux store
收到的 props
。我需要的数据还没有在商店里,所以我从componentDidMount
获取它,这是正确的。当我的减速器更新存储时,我得到了道具,因为我的组件是通过 mapStateToProps 连接的。但是页面没有渲染,状态仍然是空字符串。
例如,用户从保存的 url 加载了“编辑帖子”页面。您可以从 url 访问 postId
,但该信息尚未在 store
中,因此您可以获取它。您页面上的项目是受控组件 - 因此您显示的所有数据都在 state
中。
使用redux,获取了数据,更新了store,组件是connect
ed,但是app没有反映变化。仔细一看,收到了props
,但应用没有更新。 state
没有更新。
好吧,props
会更新和传播,但state
不会。
您需要专门告诉state
进行更新。
您不能在 render()
中执行此操作,并且 componentDidMount
已经完成了它的周期。
componentWillReceiveProps
是您更新依赖于更改的 prop
值的 state
属性的位置。
示例用法:
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
做 重新渲染!
【讨论】:
从React 16.3
开始,您should 使用getDerivedStateFromProps
而不是componentWillReceiveProps
。但事实上,你甚至不应该像here 那样做所有这些事情@
它不起作用,每当状态发生变化时,componentWillReceiveProps 就会起作用,很多情况下我尝试这不起作用。特别是多级嵌套或复杂的道具,所以我必须为组件分配一个 ID 并为其触发更改并更新状态以重新渲染新数据【参考方案3】:
据我所知,redux 唯一的作用是,如果您的组件依赖于突变状态,则在更改商店状态时调用 componentWillRecieveProps,然后您应该强制您的组件更新 是这样的
1-store State change-2-call(componentWillRecieveProps(()=>3-component state change))
【讨论】:
【参考方案4】:此答案是 Brian Vaughn 题为 You Probably Don't Need Derived State(2018 年 6 月 7 日)的文章的摘要。
从 props 派生状态是所有形式的反模式。包括使用旧的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 );
// ...
【讨论】:
以上是关于redux 连接的组件如何知道何时重新渲染?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 redux 让根组件在 react-native 中重新渲染(开源项目)