【问题标题】:Prevent re-render on a PureComponent when passing an object传递对象时防止在 PureComponent 上重新渲染
【发布时间】:2020-01-02 11:25:30
【问题描述】:

当使用PureComponents 时,您拥有优于功能组件的优势,即在父更新时组件并不总是呈现。它实际上只在组件 props 发生变化时才会呈现,在本例中仅在您更改输入时才会呈现。

如何将对象传递给 PureComponent 而不会破坏使用此类组件的所有优势?

这是否意味着一旦你有一个你期望它是 object 类型的 prop,最好让你的组件正常工作?

我添加了一个例子来说明我的意思......(可能你需要在新窗口中打开它,因为有很多不同的孩子)

class TestPureComponent extends React.PureComponent {
  render() {
    return <div style={{border: '1px solid black'}}>{this.props.text} : {this.props.inputValue} {Math.random()} <button onClick={() => {this.props.dismissClicked()}}>Click me</button>  </div>
  }
}

function TestFunctionalComponent () {
  return <div style={{border: '1px solid black'}}>I always update as I am a functional component {Math.random()}</div>
}

const hell = () => {console.log('Logs hello')}

class RenderExample extends React.Component {
  constructor (props) {
    super(props)
    this.state = {clicked: false, inputValue: 'inputValue'}
    this.onClick = this.onClick.bind(this)
    this.doSomething = this.doSomething.bind(this)
  }

  onClick () {
    this.setState({clicked: !this.state.clicked})
  }
  
  doSomething () {
    console.log('helllllo')
  }
  
  heee = () => {
    console.log('heeeee')
  }

  render () {
    return <div>
      <button onClick={this.onClick}>
        Update state (forces re-render) {this.state.clicked && 'clicked'}
      </button>
      <input onChange={(e) => this.setState({inputValue: e.target.value})} type="text" value={this.state.inputValue}/>
      <br/>
      <br/>
      <TestFunctionalComponent />
      <br/>
      <TestPureComponent dismissClicked={() => hell} inputValue={this.state.inputValue} text="If you click the button this will re-render, if you change the input this will re-render"/>
      <br/>
      <TestPureComponent text="If you click the button this will NOT re-render, if you change the input this will re-render" dismissClicked={this.doSomething} inputValue={this.state.inputValue}/>
      <br/>
      <TestPureComponent text="If you click the button this will NOT re-render, if you change the input this will re-render" dismissClicked={this.heee} inputValue={this.state.inputValue}/>
      <br/>
      <TestPureComponent text="If you click the button this will NOT re-render, if you change the input this will re-render" dismissClicked={hell} inputValue={this.state.inputValue}/>
      <br/>
      <TestPureComponent text="If you click the button this will NOT re-render, if you change the input this will re-render" dismissClicked={hell} inputValue={this.state.inputValue}/>
      <br/><br/>
      <div> we will now add an inline object to each component and now they all update</div>
      
      <TestPureComponent dismissClicked={() => hell} inlineOBJ={{hello: 'world'}} inputValue={this.state.inputValue} text="If you click the button this will re-render, if you change the input this will re-render"/>
      <br/>
      <TestPureComponent text="If you click the button this will re-render, if you change the input this will re-render" inlineOBJ={{hello: 'world'}} dismissClicked={this.doSomething} inputValue={this.state.inputValue}/>
      <br/>
      <TestPureComponent text="If you click the button this will re-render, if you change the input this will re-render" inlineOBJ={{hello: 'world'}} dismissClicked={this.heee} inputValue={this.state.inputValue}/>
      <br/>
      <TestPureComponent text="If you click the button this will re-render, if you change the input this will re-render" inlineOBJ={{hello: 'world'}} dismissClicked={hell} inputValue={this.state.inputValue}/>
      <br/>
      <TestPureComponent text="If you click the button this will re-render, if you change the input this will re-render" inlineOBJ={{hello: 'world'}} dismissClicked={hell} inputValue={this.state.inputValue}/>
    </div>
  }
}

ReactDOM.render(<RenderExample />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>

我想看看是否有办法拥有一个 PureComponent,当有一个作为道具传入的 OBJECT 时,它不会总是更新。

【问题讨论】:

  • '当使用 PureComponents 时,您比功能组件具有优势,即在父更新时组件并不总是呈现。'它不是那样工作的。 PureComponent 和 Component 之间的区别在于,第一个没有 shouldComponentUpdate 方法,而是在底层实现它并进行浅比较。我不完全理解你想要达到的目标。
  • 试试const TestFunctionalComponent = React.memo(function(){...),你会发现它永远不会更新。功能组件可以与container 一起使用,因此即使您传递了函数/对象,如果值没有改变,道具也永远不会改变引用。如果您的道具是计算对象,我建议使用容器和 React.memo。
  • @AndrzejZiółek 我想要的是有一个 PureComponent,当有一个作为道具传入的 OBJECT 时它不会总是更新(我会尝试重新表述问题)
  • 所以不要在 render 中内联构造对象 - 这样每次引用都会有所不同。
  • 正如skyboyer 所建议的,不要在渲染中构造对象。如果您必须计算或合并值来创建道具,请使用容器和React.memo。如果你渲染像 onDelete={createDelete(id)}onDelete={()=&gt;anything}item={{...dataItem,...editItem}}item={{...item,updating}} 这样的道具,你需要一个容器寻找 react-redux-connecthooksreact-redux-hooks 以了解如何编写容器。

标签: javascript reactjs


【解决方案1】:

您必须将该对象存储在某处,这样它就不会在每次重新渲染时生成。这也适用于在渲染期间生成的函数(dismissClicked={() => hell})。

如果渲染函数,函数应该在外部,这样就不会像这样为每个渲染创建它们: dismissClicked={this.hell}

对于对象,只需将其保存在状态中即可。

您可以使用功能组件实现完全相同的效果。使用 memo 包装组件以启用浅比较。

为了防止在每次渲染时生成函数,请使用 useCallback 和 useState 来保存您传递给子级的对象,以便引用保持不变。

您可以使用 useEffect 轻松更新这些对象。

希望这会有所帮助。编码愉快。

【讨论】:

    猜你喜欢
    • 2020-07-04
    • 2018-12-26
    • 2019-08-05
    • 1970-01-01
    • 2021-09-02
    • 2020-07-25
    • 1970-01-01
    • 2019-04-19
    • 1970-01-01
    相关资源
    最近更新 更多