getValuesとwatchとuseWatachの使い分け
これらの差異は何で、どういう基準で使い分ければよいのか?
結論
パフォーマンスの観点(renderingを引き起こすかどうか)で差異がある
使い勝手はどれもほぼ同じ
検討の順序としては、以下の順序で見ると良い
getValues()→useWatch()→watch()
パフォーマンス的にはgetValues()が最も良い
3つのAPIの基本概念
useFormのgetValues()
実行時に、再renderingを引き起こさない
入力の変更をsubscribeしない
ただ値を取得したいときはこれを使えばいい
例えば、「このボタンをクリックした時にフォームの値が欲しい」とか
getValues以外のことが原因で再renderingされた時に初めて値が更新される
rootではなく、呼び出した箇所で再renderingが起きる
実行順序が重要
custom hooksでかつ、getValuesで無理ならこれを使うのが良い
useFormのwatch()
formのrootで、再reneringを引き起こす
formの値が更新されると、renderingを引き起こす
This API will trigger re-render at the root of your app or form, consider using a callback or the useWatch api if you are experiencing performance issues.
useWatchの方はわかった
watch result is optimised for render phase instead of useEffect's deps, to detect value update you may want to use an external custom hook for value comparison.
useFormを使っている箇所と、watchを使っている箇所が同一のcomponentなら、useWatch/watchのいずれを使っても全く同じ挙動になるはず
でもそれだと、watch消して、useWatchの使用を強制させても良い気もすけど
ほとんど使うことない気がしているmrsekut.icon
getValues()だと困るケース
例えば、普通にsetValues()で値を更新したときは、再rendringは引き起こされない
setValuesとrenderringにも条件があってちょっとややこしいがmrsekut.icon
だから、例えばこんなhooksを作った時に
code:ts
const useItemForm = () => {
const { setValue, getValues } = useFormContext<{ items: Item[] }>();
const items = getValues('items');
const addItem = (item: Item) => {
};
return { items, addItem };
};
addItemでアイテムを追加できるが、追加した際に、renderingは引き起こされない(setValueの条件による)
そのため、itemsを購読し続けても、画面上にはその変更が反映されない
上のコードみたいに、getValues()をhooksやcomponentの第1階層目に書くことはほとんどないと思うmrsekut.icon
使うとしたら以下のように、何かしらのhandlerの中で値を取得する時に使う
code:ts
const useCommentForm = () => {
...
const insertReplyUsername = useCallback(
(username: string) => {
const comment = getValues('comment');
setValue('comment', ${comment} @${username} );
},
);
return {..}
}
感覚的には、ReacoilのuseRecoilCallbackと近いmrsekut.icon
watch()だと、root(根本のuseFormを呼び出している箇所)から再renderingされる
例えば、こういう構造のとき、
code:ts
const Root = () => {
const methods = useForm(..)
// ★ここがrenderingされるか?
return (
<FormProvider {...methods}>
<ChildForm1 />
<ChildForm2 />
</FormProvider>
);
}
const ChildForm1 = () => {
// ① watchを使った場合
const {watch} = useFormContext();
const items = watch('items')
// ② useWatchを使った場合
// const items = useWach({name: 'items'})
// ★ここがrenderingされるか?
return (..)
}
ChildForm1の中でformの値であるitemsを見たい時に、watchを使うか、useWatchを使うかで
再renderingが引き起こされる場所が異なってくる
watchを使った場合
Rootが再renderingされる
そのため、ChildForm1とは関係ないはずの、ChildForm2も再renderingされてしまう
一方で、useWatchを使った場合は、
Rootは再renderingされず、
ChildForm1の中でのみ、再renderingされる