React Lifecycleを一通り検証して見る
概要
とりあえずcomponentDidMount()使っとけばいいでしょ。
てな温度感で使ってて良くないなと思ったのでちゃんとLifecycle全部使ってみた。
公式での説明
★を使うのはレアケースらしい。
mounting時
- constructor(props)
- stateの初期化、メソッドのbindをおこなう
- static getDerivedStateFromProps(props, state) ★
- propsとstateをもとにstateを更新!
- stateを更新しない場合nullを返却
- render()
- componentDidMount()
- component mount直後に呼び出される
- リモートからデータをロードするなどに最適な場所
updating時
- static getDerivedStateFromProps(props, state) ★
- shouldComponentUpdate(nextProps, nextState) ★
- 新しいprops, stateをもとにcomponentをupdateするかどうか判断
- true返却でアップデート、false返却でアップデートしない
- render()
- getSnapshotBeforeUpdate(prevProps, prevState) ★
- 前回のpropsとstateをもとにsnapshotを作成
- 作成したsnapshotはcomponentDidUpdateで受け取ることができる
- 公式では画面スクロールで使えるとかどうとか言ってた
- componentDidUpdate(prevProps, prevState, snapshot)
- component update直後に呼び出される
- さっき記載したsnapshotを受け取れる!!
Unmounting時
- componentWillUnmount()
- component unmount直前に呼び出される
- クリーンナップ作業をここでやるらしい
Error Handling時
- static getDerivedStateFromError(error)
- error発生したら実行される
- stateのエラー更新して、エラー画面表示するとか
- componentDidCatch(error, info)
- エラー取得してエラー記録サービスに連携したりできる
実際の実験
実際に実験してみたんでメモしとく
import React from "react"; import Lifecycle from "./Lifecycle"; interface State { id: string; } class App extends React.Component<{}, State>{ constructor(props: {}) { super(props); this.state = { id: "", }; this.handleInput = this.handleInput.bind(this); } public render() { return ( <Lifecycle id={this.state.id} handleInput={this.handleInput} /> ); } private handleInput(event: React.ChangeEvent<HTMLInputElement>) { const { value } = event.target; this.setState({ id: value, }); } } export default App;
import React from "react"; interface Props { id: string; handleInput: (event: React.ChangeEvent<HTMLInputElement>) => void; } interface State { status: string; hasError: boolean; } class Lifecyle extends React.Component<Props, State>{ constructor(props: Props) { super(props); console.log("constructor called"); this.state = { status: "", hasError: false } } static getDerivedStateFromProps(props: Props, state: State) { console.log("getDerivedStateFromProps called"); if (props.id === "target") { console.log("state is changed"); return { status: "state changed", }; } console.log("state is not changed"); return null; } public shouldComponentUpdate(nextProps: Props, nextState: State) { console.log("shouldComponentUpdate called"); if (this.props.id !== nextProps.id) { console.log("current prop id !== next prop id let's update component!"); return true; } console.log("current prop id === next prop id don't update component!"); return false; } static getDerivedStateFromError(error: Error) { return { hasError: true, } } public render() { const { id, handleInput } = this.props; const {status} = this.state; if (this.state.hasError) { return ( <div>error happened</div> ); } else { return ( <> <input type="text" value={id} onChange={handleInput} /> <div>{status}</div> </> ); } } /** * componentがmountされた直後に実行 * リモートからデータロードするときに良い * ここでsetStateすべきではない。2回renderして遅くなる */ public componentDidMount() { console.log("componentDidMount called"); } public getSnapshotBeforeUpdate(prevProps: Props, prevState: State) { console.log("getSnapshotBeforeUpdate called"); if (prevProps.id === "target") { console.log("prevProps.id === target carry snapshot to update component!"); return true; } console.log("prevProps.id !== target don't carry snapshot to update component!"); return null; } /** * componentがアップデートされた直後に実行 * 前回の引数と今回の引数を比較して処理を判定 * setStateするときは分岐に入れないと無限ループ * getSnapshotBeforeUpdate()がある場合snapshot引数が有効 */ public componentDidUpdate(prevProps: Props, prevState: State, snapshot: boolean) { console.log("componentDidUpdate called"); if (this.props.id !== prevProps.id) { console.log("prev prop id !== curren prop id do something after update component"); } else if (snapshot) { console.log("true snapshot is carried"); } } /** * componentがunmountされる直前に実行 * クリーンアップ作業を一通り実行 * もうコンポーネントもいなくなるのでsetStateここで呼ばないで */ public componentWillUnmount() { console.log("componentWillUnmount called"); } public componentDidCatch(error: any, info: any) { console.log("componentDidCatch called"); } } export default Lifecyle;
まとめ
基本的にはレアケース使わずシンプルな構成に保ちたいのだ