🧚‍♀️

React Hook FormでdefaultValuesが反映されない時に確認するところ

2023/10/07に公開
2

この記事について

React Hook Formを利用していると、たまにdefaultValuesが反映されないことがあります。
大抵はうまくいきますが、たまに「あれ、反映されないな?」となるので原因と解決策をまとめておきます。

開発環境

Next.jsを利用しています。
React Hook FormはV6系〜V7系までが対象です。

defaultValuesがセットされないコード

APIをリクエストした後に、dataを取得し、そのdatadefaultValuesにセットしています。

 const { data, loading } = useNameQuery();
 const {
    control,
    formState: { errors },
    getValues,
    handleSubmit,
    register,
    setValue,
  } = useForm<any>({
    mode: 'onChange',
    defaultValues: {
      firstName: data?.firstName,
      lastName: data?.lastName
  })
  
  return (
    <input {...register('firstName')} />  // undefinedになる
    <input {...register('lastName')} />  // undefinedになる
  )

考えられる原因

defaultValuesは一度レンダリングされた値をキャッシュします。
一度キャッシュされてしまうと、その後も動的に値は変わらないので、APIのレスポンスが取得された値をセットしたとしても値は変わりません。

defaultValues are cached on the first render within the custom hook. If you want to reset the defaultValues, you should use the reset api.

https://legacy.react-hook-form.com/v6/api

解決方法1

ドキュメントにあるように、resetsetValue等を利用して動的に値を変化させるコードを書く。

 const { data, loading } = useNameQuery();
 const {
    control,
    formState: { errors },
    getValues,
    handleSubmit,
    register,
    setValue,
  } = useForm<any>({
    mode: 'onChange',
    defaultValues: {
      firstName: data?.firstName,
      lastName: data?.lastName
  })
  
  useEffect(() => {
    if (data) {
      setValue('firstName', data?.firstName)
      setValue('lastName', data?.lastName)
    }
  }, [setValue, data])
  
  
  return (
    <input {...register('firstName')} />
    <input {...register('lastName')} />
  )

解決方法2

V6系だと上記の方法が解決策になるかと思いますが、少し調べてみるとV7.40.0-next.0からレスポンスの値をdefaultValuesにセットする機能(values)が追加されたので以下の方法で解決できるようです。(まだプロジェクトで利用したことはない)

// set default value sync
function App({ values }) {
  useForm({
    values, // will get updated when values props updates
  })
}

function App() {
  const values = useFetch("/api")

  useForm({
    defaultValues: {
      firstName: "",
      lastName: "",
    },
    values, // will get updated once values returns
  })

これは最高なアップデート!!!!!🎉🎉🎉🎉🎉
リリースノートを確認するとスタンプがたくさん付いていてみんな大喜びしていました🎊🎊

https://github.com/react-hook-form/react-hook-form/releases/tag/v7.40.0-next.0

おわりに

もし他にも解決策ある場合は教えていただけるとありがたいです。
どなたかの参考になれば嬉しいです!

Discussion

Honey32Honey32

かなりパワープレイですが、

  • データフェッチするための親コンポーネントを作る
  • key にオブジェクトをハッシュ化して渡す
    • → オブジェクトが変更されるたびに key に渡される文字列が変わる
      • (ハッシュ関数を使っているので、「深い比較」になる)
    • → key が変わるので HogeForm がアンマウント→再マウントされる

という方法があると思います。(できるだけ React Hook Form の予測しづらい機能に振り回されたくないので、僕はこの方法が好きです。)

import hash from 'stable-hash'


const { data, loading } = useNameQuery();

return <HogeForm key={hash(data)} defaultValues={data} />

RHF の defaultValue は、useState の初期値と似た挙動を取るので、同様に考えた結果です。

https://ja.react.dev/reference/react/useState#resetting-state-with-a-key

けーなけーな

このような方法があるとは初めて知りました!
教えてくださりありがとうございます!

useStateと同じような挙動だとわかりやすくて良いですね!!
この方法も開発に取り入れてみようと思います!!!💪