💤

動的でネストされたフォームをuseFormContextとuseFieldArray使って実装しようとして出来ていない話

2024/03/22に公開

今個人開発しているサービスで、プロジェクトの関与者を投稿するための動的でネストされたフォーム(dynamic nested form?)を作っています。そのフォームをもっと自由な感じにしたく、useFieldArrayを使い、setStateで更新するのではなくてuseFormContextを使おうとして難航している状況について記載したいと思います。

こんなフォームを作りたい

下記のようにカテゴリに「関わった人」や「関わったチーム」を紐づけ、「関わったチーム」には「関わった人」を紐づけ、カテゴリに「サブカテゴリ」も紐づけてカテゴリと同様のことが出来るようにしたいと思っています。

Caregory Member
Category Team
Category Team [Member....]
Category Team [Member...] SubCategory Team
Category Team [Member...] SubCategory Team [Member...]
Category Team SubCategory Team [Member...]
Category Member SubCategory Team
Category Member SubCategory Team [Member...]
Category Member SubCategory [Member]
...

現状の実装

与えられた配列データをバケツリレーで親から子、子から孫、孫からひ孫に渡す
ひ孫でreact-hook-formのuseFormを使い配列要素の追加/更新/削除。
ひ孫でデータが変更されたら、サーバー側にPOST、親までバケツリレーをして、親はsetStateして画面を更新

現在の実装の画面イメージ

現在の実装の課題

フォームを理想状態に近づけようとするときに、実装を変更するのが面倒くさい。

新実装案

与えられた配列データはFormProviderのdefaultValuesに渡す
親・子・孫・ひ孫コンポーネントはuseFormContextでそのデータを参照
useFieldArrayにその参照を渡し動的に新しい配列要素を追加できるようにする
配列要素が追加/更新/削除はuseFieldArrayを使って行う

新実装案に近い実装

https://zenn.dev/maro12/articles/7d011d3dfed5d4
https://github.com/karthickthankyou/deeply-nested-form/

試した実装

新実装案を試した実装はこんな感じです。
https://github.com/placeof/creators-dynamic-nested-form

新実装案を捨てた理由

  • 新実装案に近い実装だとForm全体をsubmitしなければならない。Formのフィールドが数百になる可能性があるため、フィールドごとに追加/更新/削除をsubmit出来るようにしたい。 そのときに、useFieldArrayでappendしたコンポーネント内でuseFormを使う実装しか思いつかなかった。そのコンポーネント内で定義したuseFormを使いデータを追加/更新/削除すると、useFormContextに反映出来ない。問題はなさそうだが、何となく気持ち悪い。
  • useFieldArrayで再帰的に書こうとすると型情報とインデックス情報が上手く書けないので冗長的になる。結局今の実装でフォームを追加する方が簡単。多分黒魔術を使えばどうにかなるが、メンテが面倒くさそう。
  • 子要素が存在するときは、親要素は削除出来ないようにしたい。useWatchで子要素の数を判定するみたいな実装が面倒くさそう。useFieldArrayのfields.lengthではその判定が出来ない。
  • 完全に満足する実装にならないときに、Server Actionsを使ったやり方も出てくるかもしれないので一旦pendingした方が良い気がした。

おわりに

良ければ良い実装教えて下さい。

Discussion