はじめに
現在ポートフォリオを作成していますが、React Hook Formを使った編集フォームで、「一覧データは更新されているのに、編集ダイアログのフォームには古いデータが表示される」という問題に遭遇しました。
この記事では、その原因と解決策を紹介致します。
バージョンv7.40.0は2023年度の対応なので今更ではありますが、
対応の備忘録として記載させて頂きます。
問題の概要
Next.js の App Router + Server Actions を使った会社管理システムで以下のような現象が発生:
function CompanyList() {
const companies = await getCompanies();
return (
<div>
{companies.map(company => (
<div key={company.id}>
<span>{company.companyName}</span> {/* ← 更新されている */}
<UpsertCompanyDialog
type="edit"
data={company} // ← このデータも最新
/>
</div>
))}
</div>
);
}
// 編集ダイアログ
function UpsertCompanyDialog({ type, data }) {
const form = useForm({
defaultValues: data // ← なぜか古いデータのまま...
});
}
現象
- ✅ データベースは正常に更新される
- ✅
revalidatePath()でサーバーキャッシュも更新される - ✅ 一覧画面には最新データが表示される
- ❌ 編集フォームだけ古いデータが表示される
原因の解明
ダイアログの場合の問題
// ダイアログの場合
function CompanyList() {
const companies = getCompanies(); // revalidatePathで更新される
return (
<div>
{companies.map(company => (
// ダイアログコンポーネントは既存のまま(再マウントされない)
<UpsertCompanyDialog data={company} /> // propsは更新されるが...
))}
</div>
);
}
React Hook Form の defaultValues は初回マウント時にのみ評価されるため、その後 props が変わっても再評価されません。
つまり:
- ダイアログ: 既存のコンポーネント = 既存のフォーム = 古い defaultValues
従来の解決策
従来はuseEffectを利用して、値を検知したり、
form.resetを利用して明示的にフォームをリセットする必要がありましたが、
v7.40.0以降のリリースにてvaluesを利用してフォームの値を更新することが可能になりました。
解決策 (v7.40.0以降)
import { useForm } from 'react-hook-form';
export const useCompany = ({ type, data }) => {
const form = useForm({
defaultValues: {
companyName: '',
domain: '',
},
values: type === 'edit' && data
? {
companyName: data.companyName,
domain: data.domain,
}
: undefined, // 新規作成時は undefined
resolver: zodResolver(CompanySchema),
});
const onSubmit = async (formData) => {
const result = await (type === 'edit' ? editCompany : addCompany)(formData);
if (result.success) {
toast.success('更新が完了しました');
}
};
return { form, onSubmit };
};
めちゃくちゃ便利ですね!
参考リンク
この記事が同じ問題で悩んでいる方の助けになれば幸いです! 🎉