【问题标题】:Trigger child re-rendering in React.js在 React.js 中触发子重新渲染
【发布时间】:2015-07-14 01:59:19
【问题描述】:

父组件(在我的示例中为MyList)组件通过子组件(MyComponent)呈现一个数组。 Parent决定更改数组中的属性,React触发child重新渲染的方式是什么?

在调整数据后,我想出的只是父级中的this.setState({});。这是 hack 还是触发更新的 React 方式?

JS 小提琴: https://jsfiddle.net/69z2wepo/7601/

var items = [
  {id: 1, highlighted: false, text: "item1"},
  {id: 2, highlighted: true, text: "item2"},
  {id: 3, highlighted: false, text: "item3"},
];

var MyComponent = React.createClass({
  render: function() {
    return <div className={this.props.highlighted ? 'light-it-up' : ''}>{this.props.text}</div>;
  }
});

var MyList = React.createClass({
  toggleHighlight: function() {
    this.props.items.forEach(function(v){
      v.highlighted = !v.highlighted;
    });

    // Children must re-render
    // IS THIS CORRECT?
    this.setState({});
  },

  render: function() {
    return <div>
      <button onClick={this.toggleHighlight}>Toggle highlight</button>
      {this.props.items.map(function(item) {
          return <MyComponent key={item.id} text={item.text} highlighted={item.highlighted}/>;
      })}
    </div>;
  }
});

React.render(<MyList items={items}/>, document.getElementById('container'));

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    这里的问题是您将状态存储在this.props 而不是this.state。由于这个组件正在变异items,items 是状态并且应该存储在this.state 中。 (这里是good article on props vs. state。)这解决了您的渲染问题,因为当您更新items 时,您将调用setState,这将自动触发重新渲染。

    这是使用 state 而不是 props 的组件的样子:

    var MyList = React.createClass({
        getInitialState: function() {
            return { items: this.props.initialItems };
        },
    
        toggleHighlight: function() {
            var newItems = this.state.items.map(function (item) {
                item.highlighted = !item.highlighted;
                return item;
            });
    
            this.setState({ items: newItems });
        },
    
        render: function() {
            return (
                <div>
                    <button onClick={this.toggleHighlight}>Toggle highlight</button>
                    { this.state.items.map(function(item) {
                        return <MyComponent key={item.id} text={item.text} 
                                 highlighted={item.highlighted}/>;
                    }) }
                </div>
            );    
        }
    });
    
    React.render( <MyList initialItems={initialItems}/>,
                  document.getElementById('container') );
    

    请注意,我将 items 属性重命名为 initialItems,因为它清楚地表明 MyList 会改变它。这是recommended by the documentation。

    你可以在这里看到更新的小提琴:https://jsfiddle.net/kxrf5329/

    【讨论】:

    • 我这几天一直在拉头发,因为我不知道这样做的最佳方法是什么。我知道 state
    • “props 和 state 更改都会触发渲染更新”来自您的文章链接 github.com/uberVU/react-guide/blob/master/props-vs-state.md
    • 有时您需要使用状态不起作用的组件外部的数据。就我而言,它是一个外部样式表。甚至 this.forceUpdate() 也不起作用。
    【解决方案2】:

    我找到了一个很好的解决方案,使用 key 属性通过 React Hook 重新渲染。如果我们更改了子组件或 React 组件的某些部分的 key 属性,它将完全重新渲染。当您需要重新渲染 React 组件的某些部分或重新渲染子组件时,它将使用它。这是一个例子。我将重新渲染整个组件。

    import React, { useState, useEffect } from "react";
    import { PrEditInput } from "./shared";
    
    const BucketInput = ({ bucketPrice = [], handleBucketsUpdate, mood }) => {
      const data = Array.isArray(bucketPrice) ? bucketPrice : [];
      const [state, setState] = useState(Date.now());
      useEffect(() => {
        setState(Date.now());
      }, [mood, bucketPrice]);
      return (
        <span key={state}>
          {data.map((item) => (
            <PrEditInput
              key={item.id}
              label={item?.bucket?.name}
              name={item.bucketId}
              defaultValue={item.price}
              onChange={handleBucketsUpdate}
              mood={mood}
            />
          ))}
        </span>
      );
    };
    
    export default BucketInput;
    

    【讨论】:

    • 为组件的 key props 设置新值将重新渲染该组件
    • 关键是关键!
    • 太棒了!这节省了我的时间,只是想找出这里发生了什么魔法
    • 老实说,这应该被认为是一种设计模式。
    【解决方案3】:

    您应该通过调用setState() 并提供您想要向下传播的新道具来触发重新渲染。 如果你真的想强制更新你也可以拨打forceUpdate()。

    如果您查看此page 上的示例,您可以看到setState 是用于更新和触发重新渲染的方法。 documentation 也清楚地说明了(啊哈哈!)。

    在你的情况下,我会打电话给forceUpdate。

    编辑:正如 Jordan 在评论中提到的,最好将项目存储为您的状态的一部分。这样您就不必调用forceUpdate,但您会真正更新组件的状态,因此具有更新值的常规setState 会更好地工作。

    【讨论】:

    • 我来这里是为了验证这个方法。文档提到这一点:&gt;Normally you should try to avoid all uses of forceUpdate() 所以我坚持使用setState。我希望孩子们对财产变化做出反应。在这种情况下,如何在 Parent 中保存突出显示的项目列表?我来自 Backbone,所以,对于 React 世界来说,这些问题听起来很愚蠢。
    • 在您的情况下,调用 forceUpdate 似乎更好。你不是在修改你的状态,而是你的道具。调用 forceUpdate 是可行的方法。
    • 我不确定你想做什么,但是在查看你的代码之后,你没有任何状态,一切都是基于 props 的。不过我会做不同的事情。如果必须突出显示,我会让每个孩子负责跟踪。
    • “你没有任何状态”不正确。 this.props.item[n].highlighted 是定义状态,因为它不是从父组件传下来的,而是被这个组件变异的。
    • 问题不是“React 术语”(这是明确的),问题是这个数据应该进入状态,而不是道具,因为它是状态。这是一篇关于道具与状态的好文章:github.com/uberVU/react-guide/blob/master/props-vs-state.md
    【解决方案4】:

    重新渲染孩子的一个简单选择是在每次需要重新渲染时更新唯一键属性。

    <ChildComponent key={this.state.updatedKey}/>
    

    【讨论】:

      【解决方案5】:

      您可以在子组件上设置数字键,并在执行操作后触发键更改。例如

      state = {
              childKey: 7,
      };
      
      
      
      <ChildComponent key={this.state.childKey}/>
      
      
      actionToTriggerReload = () => {
      const newKey = this.state.childKey * 89; // this will make sure the key are never the same
      this.setState({childKey: newKey})
         }
      

      这肯定会重新渲染 ChildComponent

      【讨论】:

      • 它工作正常,谢谢
      【解决方案6】:

      在子组件中设置一个数字默认“键”并重新渲染只需更改键值。

      this.state = {
              updatedKey: 1,
      };    
      triggerReload = () => {
      let newKey = Math.floor(Math.random() * 100); // make sure the key are never the same
      this.setState({updatedKey: newKey})
         }
      <childComponent key={this.state.updatedKey} handlerProp = {this.onClickItemEvent} />
      

      这对我在 reactjs 类库中重新渲染 ChildComponent 很有用

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-01
        • 2018-01-30
        • 1970-01-01
        • 1970-01-01
        • 2011-07-27
        • 2018-03-03
        相关资源
        最近更新 更多