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

时间:2016-11-02 17:27:48

标签: reactjs redux react-redux

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

这是我的理解。
在一个天真的反应组件中,我们有states& props。使用state更新setState会重新呈现整个组件。 props大部分都是只读的,更新它们并不合理。

在通过类似store.subscribe(render)之类的订阅redux商店的react组件中,显然每次更新商店时都会重新呈现。

react-redux有一个帮助器connect(),它将部分状态树(组件感兴趣)和actionCreators注入props组件,通常是通过类似的东西

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

但是要理解setState对于TodoListComponent对还原状态树更改(重新渲染)做出反应至关重要,我无法找到任何statesetState组件文件中的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)捆绑在一起的待办事项列表示例。

4 个答案:

答案 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;

  

componentWillReceivePropsstate与新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 });
  }

  // ...
}