ガイド / ネストされたオブジェクトと配列

ネストされたオブジェクトと配列

Conform は、 name 属性の命名規則を活用することで、ネストされたオブジェクトと配列の両方をサポートしています。

#命名規則

Conform は、データ構造を示すために object.property および array[index] の構文を使用します。これらの表記法は、ネストされた配列に対しても組み合わせることができます。例えば、 tasks[0].content のようになります。フォームデータに ['tasks[0].content', 'Hello World'] というエントリがある場合、構築されるオブジェクトは { tasks: [{ content: 'Hello World' }] } になります。

しかし、各フィールドの name 属性を手動で設定する必要はありません。 Conform は常に名前を推測してくれるため、生成された名前を全て使用していれば、より良い型安全性が得られます。

#ネストされたオブジェクト

ネストされたフィールドを設定するには、親フィールドのメタデータから getFieldset() メソッドを呼び出し、名前が自動的に推測される各子フィールドにアクセスしてください。

1import { useForm } from '@conform-to/react';
2import { parseWithZod } from '@conform-to/zod';
3import { z } from 'zod';
4
5const schema = z.object({
6  address: z.object({
7    street: z.string(),
8    zipcode: z.string(),
9    city: z.string(),
10    country: z.string(),
11  }),
12});
13
14function Example() {
15  const [form, fields] = useForm({
16    onValidate({ formData }) {
17      return parseWithZod(formData, { schema });
18    },
19  });
20  const address = fields.address.getFieldset();
21
22  return (
23    <form id={form.id}>
24      {/* name を `address.street` 、`address.zipcode` などに設定します。 */}
25      <input name={address.street.name} />
26      <div>{address.street.errors}</div>
27      <input name={address.zipcode.name} />
28      <div>{address.zipcode.errors}</div>
29      <input name={address.city.name} />
30      <div>{address.city.errors}</div>
31      <input name={address.country.name} />
32      <div>{address.country.errors}</div>
33    </form>
34  );
35}

#配列

フィールドのリストを設定する必要がある場合は、親フィールドのメタデータから getFieldList() メソッドを呼び出して、名前が自動的に推測される各アイテムフィールドにアクセスできます。リスト内のアイテムを変更したい場合は、 Intent button ページで説明されているように、 insertremovereorder のインテントも使用できます。

1import { useForm } from '@conform-to/react';
2import { parseWithZod } from '@conform-to/zod';
3import { z } from 'zod';
4
5const schema = z.object({
6  todos: z.array(
7    z.object({
8      title: z.string(),
9      notes: z.string(),
10    }),
11  ),
12});
13
14function Example() {
15  const [form, fields] = useForm({
16    onValidate({ formData }) {
17      return parseWithZod(formData, { schema });
18    },
19  });
20  const tasks = fields.tasks.getFieldList();
21
22  return (
23    <form id={form.id}>
24      <ul>
25        {tasks.map((task) => (
26          <li key={task.key}>
27            {/* 名前を `tasks[0]` 、 `tasks[1]` などに設定します。 */}
28            <input name={task.name} />
29            <div>{task.errors}</div>
30          </li>
31        ))}
32      </ul>
33    </form>
34  );
35}

#ネストされた配列

ネストされた配列に対して、 getFieldset()getFieldList() の両方を組み合わせて使用することもできます。

1import { useForm } from '@conform-to/react';
2
3function Example() {
4  const [form, fields] = useForm();
5  const todos = fields.todos.getFieldList();
6
7  return (
8    <form id={form.id}>
9      <ul>
10        {todos.map((todo) => {
11          const todoFields = todo.getFieldset();
12
13          return (
14            <li key={todo.key}>
15              <input name={todoFields.title.name} />
16              <div>{todoFields.title.errors}</div>
17              <input name={todoFields.notes.name} />
18              <div>{todoFields.notes.errors}</div>
19            </li>
20          );
21        })}
22      </ul>
23    </form>
24  );
25}