Redux连接的组件如何知道何时重新渲染?


86

我可能想念一些很明显的东西,想让自己清楚。

这是我的理解。
在幼稚的react组件中,我们有statesprops。更新state时会setState重新渲染整个组件。props大多是只读的,更新它们没有任何意义。

在订阅redux商店的react组件中,通过类似于store.subscribe(render),它每次商店更新时都会重新渲染。

react-redux具有一个帮助程序,它通常通过类似以下方式connect()注入状态树的一部分(组件感兴趣)和actionCreatorsprops组件

const TodoListComponent = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

但是,了解到asetState对于TodoListComponent响应redux状态树更改(重新渲染)必不可少,因此在组件文件中找不到任何statesetState相关代码TodoList。它的内容如下:

const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
)

有人可以指出我所缺少的正确方向吗?

言:我遵循与redux软件包捆绑在一起的待办事项列表示例。

Answers:


109

connect函数生成一个订阅商店的包装器组件。分派操作时,将通知包装器组件的回调。然后mapState,它将运行您的函数,并将此时的结果对象与上次的结果对象进行浅比较(因此,如果您要使用相同的值重写redux存储字段,则不会触发重新渲染)。如果结果不同,则将结果作为道具传递给“真实”组件。

Dan Abramov写了connectat(connect.js)的简化版本,它阐明了基本思想,尽管它没有显示任何优化工作。我也有许多有关Redux性能的文章的链接,这些文章讨论了一些相关的想法。

更新

React-Redux v6.0.0对连接的组件如何从存储中接收数据进行了一些重大的内部更改。

作为其中的一部分,我写了一篇文章,解释了connectAPI及其内部的工作方式以及它们随着时间的变化:

惯用的Redux:React-Redux的历史和实现


谢谢!您是前几天帮我的同一个人@ HN-news.ycombinator.com/item?id=12307621,提供了有用的链接吗?认识你很高兴:)
乔·刘易斯

4
是啊,这就是我:)我花的方式太多时间寻找的终极版讨论在线- reddit的,HN,SO,中及其他地方。乐意效劳!(也供参考,我强烈推荐Discord上的Reactiflux聊天频道。很多人在那里闲逛并回答问题。美国东部标准时间晚上我通常都在网上。邀请链接在reactiflux.com。)
markerikson

假设这回答了问题,介意接受答案吗?:)
markerikson

它是比较对象本身还是对象之前/之后的属性?如果是后者,那么它仅检查第一级,是“浅”的意思吗?
安迪

是的,“浅相等检查”意味着比较每个对象的第一级字段:prev.a === current.a && prev.b === current.b && .....。假定任何数据更改都将导致新的引用,这意味着需要不变的数据更新而不是直接突变。
markerikson

13

我的回答有点超出左领域。它揭示了导致我担任此职位的问题。就我而言,即使收到新道具,该应用似乎也没有重新渲染。
React开发人员对这个经常被问到的问题有一个答案,即(商店)发生了突变,这就是99%的时间,这就是react不会重新渲染的原因。然而,其余的1%一无所获。这里不是突变。


TLDR;

componentWillReceiveProps如何state使新的保持同步props

边缘情况:一旦state更新,然后应用程序不会再呈现!


事实证明,如果您的应用仅state用于显示其元素,则props可以更新,但state不会更新,因此无需重新渲染。

我有state那依赖于props从redux收到的信息store。我需要的数据尚未存储在存储中,因此我从componentDidMount适当地获取了数据。当我的reducer更新商店时,我拿回了道具,因为我的组件是通过mapStateToProps连接的。但是页面没有渲染,状态仍然充满空字符串。

例如,某用户从保存的网址加载了“编辑帖子”页面。您可以postId从url访问,但是该信息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组件生命周期方法-深入研究

componentWillReceiveProps是您进行更新state以与props更新保持同步的地方。

一旦state更新,然后视域state 重新呈现!


3
从此React 16.3开始,您应该使用getDerivedStateFromProps代替componentWillReceiveProps。但实际上,您甚至不应该执行此处
kyw

它不起作用,每当状态更改时,componentWillReceiveProps都将起作用,因此在很多情况下我都尝试不起作用。特别是多层嵌套或复杂的道具,因此我必须为组件分配ID并为其触发更改并更新状态以重新呈现新数据
May Weather VN

0

该答案是Brian Vaughn题为``您可能不需要派生状态''(2018年6月7日)的文章的摘要。

从道具派生状态是所有形式的反模式。包括使用旧的componentWillReceiveProps和较新的getDerivedStateFromProps

与其从道具中得出状态,不如考虑以下解决方案。

两项最佳做法建议

建议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 });
  }

  // ...
}

-2

据我所知,redux只能做的事情是,如果您的组件依赖于突变状态,则在更改商店状态时会调用componentWillRecieveProps,然后您应该强制组件更新其状态,如下所示

1存储区状态更改2-调用(componentWillRecieveProps(()=> {3组件状态更改}))

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.