从componentWillReceiveProps迁移到getDerivedStateFromProps

时间:2019-10-02 22:24:39

标签: reactjs

我正在学习reactjs,我用componentWillReceiveProps(cWRP)方法编写了component,但是我读到它已被弃用,必须替换为getDerivedStateFromProps(gDSFP)-https://en.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
请注意,以下代码的唯一目的是说明我的问题。它不是完整的代码。
App.js文件:

import React from 'react';
import './App.css';

import Display from './component.js'

class App extends React.Component {

  state={resetCounter:false}

  resetCounter= () => this.setState( {resetCounter: true} );

  render() {
      return (
      <div className="App">
        <header className="App-header">
          <Display resetCounter={this.state.resetCounter}></Display>
          <div>
           <p></p><p></p>
           <button onClick={this.resetCounter}>Reset</button>
          </div>
        </header>
      </div>
    );
  }

  componentDidUpdate () {
    if (this.state.resetCounter!==false)
      this.setState( {resetCounter: false} );
  }

}

export default App;

component.js文件

import React from 'react'

class Display extends React.Component {

  constructor() {
    super();
    this.state = this.resetState();
    this.state.generalCounter=0;
  } 

  /* method to avoid code duplication in constructor and cWRP
   could not be used with getDerivedStateFromProps */
  resetState = () => ({resettableCounter: 0,}); 

  componentWillReceiveProps(nextProps) {
    if (nextProps.resetCounter===true) 
      this.setState(this.resetState())
  }

  render() {
    return (
        <>
        <div>
        <div>general counter : {this.state.generalCounter}</div>
        <div>resettable counter : {this.state.resettableCounter}</div>
        </div>
        <div>
            <button onClick={this.incCounters}>+</button>
            <button onClick={this.decCounters}>-</button>
        </div>
        </>
    )
  }

  incCounters= () => this.setState(
    {
      resettableCounter: this.state.resettableCounter+1,
      generalCounter: this.state.generalCounter+1 
    }
  )

  decCounters= () => this.setState(
    {
      resettableCounter: this.state.resettableCounter-1,
      generalCounter: this.state.generalCounter-1
    }
  )

}

export default Display

在组件的状态下,有一个可重置的部分和一个不可重置的部分。方法resetState用于避免在构造函数和cWRP中重复代码。
为了用gDSFP替换cWRP,我编写了一个类方法,因为无法在gDSFP中调用实例方法(this不可用)

... 

  constructor() {
    super();
    this.state = Display.resetState();
    this.state.generalCounter=0;
  } 

  static resetState () {
    return ({resettableCounter: 0,}); 
  }

  static getDerivedStateFromProps(nextProps) {
    if (nextProps.resetCounter === true) {
      return Display.resetState();
    } else {
      return null;
    }
  }

...

使用此解决方案,可以很容易地修改我的所有组件,但是我不确定这是一个好方法。
我想知道我是否有误解,是否应该重写我的组件以使用键(https://en.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#preferred-solutions)将它们分为完全受控的组件和完全不受控制的组件。
例如,在这种情况下,我必须写:

  1. 可重置计数器的一个完全不受控制的组件
  2. 一个完全控制的不可重置计数器
  3. 带有+/-按钮可渲染它们的父组件。

我问这个问题是因为在某些情况下,这将需要大量工作,因此我想确定再继续。

2 个答案:

答案 0 :(得分:0)

如果您的组件依赖于某些外部道具(您无法控制)(例如,返回的JSON或第3方渲染道具组件等),则您希望在帖子中保留gdsfp版本。

您似乎完全可以控制传递给Display的内容。您可以将初始resettableCounter的值向下传递到Display

优点是双重的。

  1. 您的Display道具展示了Display的作用-使其更具描述性/可读性。
  2. 无需维护数据,因此维护起来更容易。

对于您的特定情况,Fully uncontrolled component with a key似乎更有意义,因为Display应该接受要显示的初始值,但负责管理reseetableCounter

答案 1 :(得分:0)

除非这是绝对不可避免的,否则不要创建控制其兄弟姐妹(或父母)的组件。而是将状态提升为共同祖先:

const Display = ({
  generalCounter,
  resettableCounter,
  incrementCounters,
  decrementCounters,
}) => (
  <div>
    <div>General Counter: {generalCounter}</div>
    <div>Resettable Counter: {resettableCounter}</div>
    <button onClick={incrementCounters}>Increment</button>
    <button onClick={decrementCounters}>Decrement</button>
  </div>
);

class DisplayContainer extends React.Component {
  state = {
    generalCounter: 0,
    resettableCounter: 0,
  };

  incrementCounters = () => this.setState(prevState => ({
    generalCounter: prevState.generalCounter + 1,
    resettableCounter: prevState.resettableCounter + 1,
  }));

  decrementCounters = () => this.setState(prevState => ({
    generalCounter: prevState.generalCounter - 1,
    resettableCounter: prevState.resettableCounter - 1,
  }));

  resetResettableCounter = () => this.setState({
    resettableCounter: 0,
  });

  render() {
    return (
      <React.Fragment>
        <Display
          {...this.state}
          incrementCounters={this.incrementCounters}
          decrementCounters={this.decrementCounters}
        />
        <button onClick={this.resetResettableCounter}>
          Reset Resettable Counter
        </button>
      </React.Fragment>
    );
  }
}

const App = () => (
  <div>
    <DisplayContainer />
  </div>
);

一种替代方法是类似Redux(可将状态有效地移出React)。