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

IT技术 reactjs redux react-redux
2021-04-19 15:47:35

我可能遗漏了一些非常明显的东西,并想清除自己。

这是我的理解。
在一个简单的 react 组件中,我们有states& props更新statesetState重新呈现整个组件。props大多是只读的,更新它们没有意义。

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

react-redux有一个助手connect(),它注入状态树的一部分(组件感兴趣的部分)和组件的 actionCreators props,通常通过类似的方式

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>
)

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

PS 我正在关注与redux 包捆绑在一起的待办事项列表示例

4个回答

connect函数生成一个订阅商店的包装组件。当一个动作被分派时,包装器组件的回调被通知。然后它运行你的mapState函数,并浅比较这次的结果对象和上次的结果对象(所以如果你用相同的值重写redux 存储字段,它不会触发重新渲染)。如果结果不同,那么它会将结果作为props传递给您的“真实”组件。

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

更新

React-Redux v6.0.0对连接组件从 store 接收数据的方式进行了一些重大的内部更改。

作为其中的一部分,我写了一篇文章,解释了connectAPI 及其内部结构的工作原理,以及它们如何随时间变化:

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

是的,“浅等式检查”意味着比较每个对象的第一级字段:prev.a === current.a && prev.b === current.b && ...... 这假设任何数据更改都会导致新的引用,这意味着需要不可变的数据更新而不是直接更改。
2021-05-23 15:47:35
是比较对象本身还是比较对象之前/之后的属性?如果是后者,是不是只检查第一层,是不是你说的“浅”?
2021-05-26 15:47:35
假设这回答了问题,介意接受答案吗?:)
2021-05-29 15:47:35
是啊,这就是我:)我花的方式太多时间寻找的终极版讨论在线- reddit的,HN,SO,中及其他地方。乐意效劳!(另外仅供参考,我强烈推荐 Discord 上的 Reactiflux 聊天频道。很多人在那里闲逛并回答问题。我通常在美国东部时间晚上在线。邀请链接位于reactiflux.com。)
2021-05-30 15:47:35
谢谢!您是前几天在@HN - news.ycombinator.com/item?id=12307621帮助我并提供有用链接的同一个人吗?很高兴见到你 :)
2021-06-18 15:47:35

我的答案有点超出了左场。它揭示了导致我写这篇文章的一个问题。就我而言,该应用程序似乎没有重新渲染,即使它收到了新的props。
React 开发人员对这个经常被问到的问题有一个答案,即如果 (store) 发生了变异,99% 的时间这就是 react 不会重新渲染的原因。然而,其他 1% 却一无所知。突变不是这里的情况。


TLDR;

componentWillReceiveProps是如何state与新的props.

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


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

我有state这依赖于props从 redux 收到store我需要的数据还没有在存储中,所以我从 获取它componentDidMount,这是正确的。当我的减速器更新存储时,我取回了props,因为我的组件是通过 mapStateToProps 连接的。但是页面没有渲染,状态仍然充满空字符串。

例如,假设用户从保存的 url 加载了“编辑帖子”页面。您可以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 重新呈现!

从那时React 16.3起,您应该使用getDerivedStateFromProps代替componentWillReceiveProps但事实上,你甚至不应该像这里阐明的那样做所有的事情
2021-06-02 15:47:35
它不起作用,每当状态改变时,componentWillReceiveProps 就会起作用,所以我尝试这不起作用。特别是多级嵌套或复杂的道具,所以我必须为组件分配一个 ID 并为其触发更改并更新状态以重新呈现新数据
2021-06-15 15:47:35

这个答案是对 Brian Vaughn 题为“你可能不需要派生状态”(2018 年 6 月 7 日)的文章的总结

从 props 派生 state 是所有形式的反模式。包括使用较旧的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 props重置不受控制的组件
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 唯一会做的事情是,如果您的组件依赖于变异状态,则在更改商店状态时调用 componentWillRecieveProps,然后您应该强制您的组件更新它,就像这样

1-store State change-2-call(componentWillRecieveProps(()=>{3-component state change}))