エンジニアの覚え書き

web系エンジニアの技術メモを主に投稿していきます。

React 16のライフサイクルの私的整理

React Component内で何か処理を行おうとした際、毎回koba04さんのこの記事を参照して、Reactのライフサイクル的にどこが適切な実装箇所かを確認して実装している。でも毎回調べるのは時間がもったいないので、個人のメモとしてまとめる。

lifecycle diagram

ライフサイクルの流れが図でわかる。

やりたいこと別スニペット

公式の例より。後で追記して全パターンまとめる。

stateを初期化したい

stateを定義して初期化。

class ExampleComponent extends React.Component<ExampleProps, ExampleState> {
  state = {
    currentColor: this.props.defaultColor,
    palette: 'rgb',
  };
}

データをAPIでfetchしたい

componentDidMount内に実装。

class ExampleComponent extends React.Component {
  state = {
    externalData: null,
  };

  componentDidMount() {
    this._asyncRequest = loadMyAsyncData().then(
      externalData => {
        this._asyncRequest = null;
        this.setState({externalData});
      }
    );
  }

  componentWillUnmount() {
    if (this._asyncRequest) {
      this._asyncRequest.cancel();
    }
  }

  render() {
    if (this.state.externalData === null) {
      // Render loading state ...
    } else {
      // Render real UI ...
    }
  }
}

propsの値に応じてstateを更新したい

getDerivedStateFromPropsを使う。getDerivedStateFromPropsを実装すると、戻り値でstateが更新されるらしい(nullを返却すれば更新されない)。

class ExampleComponent extends React.Component {
  // Initialize state in constructor,
  // Or with a property initializer.
  state = {
    isScrollingDown: false,
    lastRow: null,
  };

  static getDerivedStateFromProps(props, state) {
    if (props.currentRow !== state.lastRow) {
      return {
        isScrollingDown: props.currentRow > state.lastRow,
        lastRow: props.currentRow,
      };
    }

    // Return null to indicate no change to state.
    return null;
  }
}

propsの変化に応じて副作用のある処理を実行したい

componentDidUpdate内に実装する。propsが変更される場合に必ず実行されるので、prevProps, prevStateと比較して無駄に初理が実行されないように気をつける。

class ExampleComponent extends React.Component {
  componentDidUpdate(prevProps, prevState) {
    if (this.props.isVisible !== prevProps.isVisible) {
      logVisibleChange(this.props.isVisible);
    }
  }
}

propsの変化に応じて外部からデータを取得したい

getDerivedStateFromPropsでstateをpropsの変化に応じて更新し、componentDidUpdateでstateの変更を検知してデータの取得を行う。

class ExampleComponent extends React.Component {
  state = {
    externalData: null,
  };

  static getDerivedStateFromProps(props, state) {
    // Store prevId in state so we can compare when props change.
    // Clear out previously-loaded data (so we don't render stale stuff).
    if (props.id !== state.prevId) {
      return {
        externalData: null,
        prevId: props.id,
      };
    }

    // No state update necessary
    return null;
  }

  componentDidMount() {
    this._loadAsyncData(this.props.id);
  }

  componentDidUpdate(prevProps, prevState) {
    if (this.state.externalData === null) {
      this._loadAsyncData(this.props.id);
    }
  }

  componentWillUnmount() {
    if (this._asyncRequest) {
      this._asyncRequest.cancel();
    }
  }

  render() {
    if (this.state.externalData === null) {
      // Render loading state ...
    } else {
      // Render real UI ...
    }
  }

  _loadAsyncData(id) {
    this._asyncRequest = loadMyAsyncData(id).then(
      externalData => {
        this._asyncRequest = null;
        this.setState({externalData});
      }
    );
  }
}

参考