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}); } ); } }