【问题标题】:re-render single child component based on global state change without re-rendering siblings基于全局状态更改重新渲染单个子组件,而无需重新渲染兄弟姐妹
【发布时间】:2018-11-30 18:44:35
【问题描述】:

我有一个包含许多子组件的 React 父组件(数千个 SVG 元素,重新渲染成本很高)。子组件基于全局状态显示信息;某处是应该以特殊方式显示的那些子组件的列表。由于其他地方需要此列表,因此它不能是每个子节点的本地状态信息,因为它不是子节点私有的。

遵循此处的建议:https://reactjs.org/docs/lifting-state-up.html 我将状态提升到父组件。但是,一次只改变一个孩子;例如,用户可能会单击 SVG 元素来调整其显示属性(这些属性存储为孩子的道具)。但是,由于状态存储在父级中,因此在父级上调用 setState 会触发整个父级组件(包括其所有数千个子级)的昂贵重新渲染,即使它们中的大多数没有更改。

所以我不确定如何在 React 中正确执行此操作并获得良好的性能。 (我找到了 forceUpdate 方法,以及许多永远不要使用它的警告。)我想我想做的是以某种方式更新孩子的道具,但从文档中,我能看到的唯一方法父级更新单个子级的道具,是为了调用父级的render() 方法,这意味着它也在重新渲染所有未更改的兄弟姐妹,这是我想要的昂贵案例避免。

有没有办法让一个点击处理程序,存储在父级或其他地方,可以访问全局状态,改变这个状态,更新它影响的单个子组件的道具,然后只告诉那个孩子重新渲染?

对不起,我没有包含任何源代码;这实际上是用 Dart 而不是 JS 编写的,但我认为问题出在底层 React 库级别。

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    您必须使用shouldComponentUpdate()React.PureComponentReact.memo()(如果您使用的是 React > v16.6.0 和功能组件)。你想要这样的东西:

    父组件

    class Parent extends Component {
      state = {
        color1: "red",
        color2: "blue"
      };
    
      changeState = () =>
        this.setState((state, props) => ({
          color1: state.color1 === "red" ? "yellow" : "red",
          color2: state.color2
        }));
    
      render() {
        return (
          <>
            <Child color={this.state.color1} />
            <Child color={this.state.color2} />
            <button onClick={this.changeState}>Change State</button>
          </>
        );
      }
    }
    

    每次单击Change State 时,父级将在红色和黄色之间切换color1 状态属性。然后,您可以通过一些选项来优化您的应用。

    shouldComponentUpdate()

    class Child extends Component {
      shouldComponentUpdate(nextProps, nextState) {
        if (nextProps.color !== this.props.color) return true;
        return false;
      }
      render() {
        return (
          <div style={{ backgroundColor: this.props.color }}>I am a child</div>
        );
      }
    }
    

    React.PureComponent

    class PureComponentChild extends React.PureComponent {
      render() {
        return (
          <div style={{ backgroundColor: this.props.color }}>I am a child</div>
        );
      }
    }
    

    React.memo()

    const FunctionalChild = React.memo(props =>
      <div style={{ backgroundColor: props.color }}>I am a child</div>
    );
    

    欲了解更多信息,请查看shouldComponentUpdate()React.PureComponentReact.memo()

    【讨论】:

    • 谢谢!我只有第一个可用,因为 Dart react 包似乎使用 React 版本 15。我设置了 40,000 个子组件来比较时间。如果没有优化,重新渲染整个父级大约需要 6 秒,而优化后大约需要 3 秒。所以仍然有一个减速,大概是由于执行了一个循环 40,000 次迭代调用 shouldComponentUpdate。我仍然很好奇是否有一种方法不涉及将数据作为状态存储在父级中,允许触发仅 1 个子级的重新渲染而不渲染父级。
    • 大声笑,你那里有很多组件。好吧,如果您确实需要将状态保留在 Child 中并且仍然在 Parent 中知道它,但 这不是 React 方式,您可以在 parent 中编写一个 getter。
    • 如果你想要一个方向,它看起来像这样:在父级中: getChildState(childState){// do something with child state} 并将其传递给子级:&lt;Child getChildState={this.getChildState} /&gt;在孩子中: 从构造函数或 componentDidMount()componentDidUpdate() 调用 this.props.getChildState(this.state)。但同样,除非您有充分的理由这样做,否则不要这样做
    猜你喜欢
    • 2020-01-08
    • 2020-06-26
    • 2019-10-06
    • 2018-04-05
    • 2020-04-07
    • 2019-11-09
    • 2018-04-20
    • 1970-01-01
    相关资源
    最近更新 更多