👋
【React】setStateの引数に関数を渡すと何が嬉しいのか
はじめに
ReactのuseStateでstateを更新する際、引数には
- 新しいstate
- 以前のstateを引数にとり、新しいstateを返す関数
のいずれかを渡すことになります。
const [state, setState] = useState({id:1,value:''});
const setValue = (value:string) => setState({...state, value});
const [state, setState] = useState({id:1,value:''});
const setValue = (value:string) => setState((prev) => ({
...state, value
}));
どちらのパターンでも動作はしますが、後者の以前のstateを引数にとり、新しいstateを返す関数
を渡すパターンには、前者と違って「stateに依存しない」というメリットが存在します。
なぜstateへの依存を無くしたいのか
state更新用の関数がstateそのものに依存してしまうと、useCallbackによるメモ化が上手く働きません。下記がサンプルコードとなります。
const [state, setState] = useState({id:1,value:''});
const setValue = useCallback((value:string) => setState({...state, value}), [state]);
上記のsetValue関数は関数外部のstate
に依存しているため、useCallbackによるメモ化を行う際には、依存配列にstate
を含める必要があります。
それにより、stateが更新されるタイミングで関数も再生成されてしまいます。
それに対して、以前のstateを引数にとり、新しいstateを返す関数
を渡すパターンでuse
Callbackを用いる場合、下記のようなコードになります。
const [state, setState] = useState({id:1,value:''});
const setValue = useCallback((value:string) => setState((prev) => ({
...prev, value
})),[]);
依存配列からstateを除去できるため、関数のメモ化を適切に行うことが可能です。
まとめ
useCallbackによるメモ化を行わないのであれば、どちらのパターンでも正しく動作します。
しかし、再レンダリング抑制のためにチューニングが必要になった場合が問題です。
更新用の関数がstateに依存することで思わぬ挙動に繋がりますので、「メモ化された値 / 関数」が更新されるタイミングには気を配っておきましょう。
Discussion