はじめに / v1 へのアップグレード

v1 へのアップグレード

このガイドでは、 v1 で導入されたすべての変更点を説明し、既存のコードベースをアップグレードする方法をご案内します。

#最小限必要な React バージョン

Conform は現在、 React 18 以降を要求します。もし古いバージョンの React を使用している場合は、まず React のバージョンをアップグレードする必要があります。

#conform オブジェクトは削除されました

まず、すべてのヘルパーが改名され、個別にインポートできるようになりました:

以前 conform.VALIDATION_UNDEFINED および conform.VALIDATION_SKIPPED を使用していた場合、それらは zod インテグレーション (@conform-to/zod) に移されました。

conform.INTENT はもはやエクスポートされていないことに注意してください。インテントボタンを設定する必要がある場合は、より良い型安全性のために zod の z.discriminatedUnion() と組み合わせて、それを intent (または好みの何か)と名付けることができます。

オプションに関してもいくつかの破壊的変更があります:

  • getInputProps における type オプションが現在必須になりました。
1<input {...getInputProps(fields.title, { type: 'text' })} />
  • description オプションは ariaDescribedBy に改名され、ブール値の代わりに文字列型( description 要素の id )になりました。
1<input
2  {...getInputProps(fields.title, {
3    ariaDescribedBy: fields.title.descriptionId,
4  })}
5/>

#フォーム設定の変更点

まず、form.props が削除されました。代わりに getFormProps() ヘルパーを使用できます。

1import { useForm, getFormProps } from '@conform-to/react';
2
3function Example() {
4  const [form] = useForm();
5
6  return <form {...getFormProps(form)} />;
7}

useFieldset および useFieldList フックは削除されました。代わりにフィールドメタデータ上で getFieldset() または getFieldList() メソッドを呼び出すことができます。

1function Example() {
2  const [form, fields] = useForm();
3
4  // Before: useFieldset(form.ref, fields.address)
5  const address = fields.address.getFieldset();
6  // Before: useFieldList(form.ref, fields.tasks)
7  const tasks = fields.tasks.getFieldList();
8
9  return (
10    <form>
11      <ul>
12        {tasks.map((task) => {
13          // ネストされたリストを持つ追加のコンポーネントを定義する必要はなくなりました。
14          // フィールドセットに直接アクセスできるようになったためです。
15          const taskFields = task.getFieldset();
16
17          return <li key={task.key}>{/* ... */}</li>;
18        })}
19      </ul>
20    </form>
21  );
22}

validatelist のエクスポートは、フォームメタデータオブジェクトに統合されました:

1function Example() {
2  const [form, fields] = useForm();
3  const tasks = fields.tasks.getFieldList();
4
5  return (
6    <form>
7      <ul>
8        {tasks.map((task) => {
9          return <li key={task.key}>{/* ... */}</li>;
10        })}
11      </ul>
12      <button {...form.insert.getButtonProps({ name: fields.tasks.name })}>
13        Add (Declarative API)
14      </button>
15      <button onClick={() => form.insert({ name: fields.tasks.name })}>
16        Add (Imperative API)
17      </button>
18    </form>
19  );
20}

以下に、すべての同等のメソッドを示します:

  • validate -> form.validate
  • list.insert -> form.insert
  • list.remove -> form.remove
  • list.reorder -> form.reorder
  • list.replace -> form.update
  • list.append および list.prepend は削除されました。代わりに form.insert を使用できます。

#スキーマ・インテグレーション

混乱を避けるために、各統合における API を一意の名前に変更しました。こちらが同等のメソッドです:

@conform-to/zod

@conform-to/yup

#送信処理の改善

セットアップを簡素化するために、送信オブジェクトを再設計しました。

1export async function action({ request }: ActionArgs) {
2  const formData = await request.formData();
3  const submission = parseWithZod(formData, { schema });
4
5  /**
6   * 送信ステータスは「success」、「error」、または undefined のいずれかになります。
7   * ステータスが undefined の場合、送信が準備されていないことを意味します(つまり、 intent が submit ではありません)。
8   */
9  if (submission.status !== 'success') {
10    return json(submission.reply(), {
11      // また、ステータスを使用してHTTPステータスコードを決定することもできます。
12      status: submission.status === 'error' ? 400 : 200,
13    });
14  }
15
16  const result = await save(submission.value);
17
18  if (!result.successful) {
19    return json(
20      submission.reply({
21        // `reply` メソッドに追加のエラーを渡すこともできます。
22        formErrors: ['Submission failed'],
23        fieldErrors: {
24          address: ['Address is invalid'],
25        },
26
27        // or avoid sending the the field value back to client by specifying the field names
28        hideFields: ['password'],
29      }),
30    );
31  }
32
33  // `resetForm` オプションを使用して送信に応答します。
34  return json(submission.reply({ resetForm: true }));
35}
36
37export default function Example() {
38  const lastResult = useActionData<typeof action>();
39  const [form, fields] = useForm({
40    // 混乱を避けるために、 `lastSubmission` は `lastResult` に改名されました。
41    lastResult,
42  });
43
44  // フォームメタデータからも送信のステータスを確認できるようになりました。
45  console.log(form.status); // "success", "error" or undefined
46}

#useInputControl フックを使用したインテグレーションがシンプルに

useInputEvent フックは、いくつかの新機能を備えた useInputControl フックに置き換えられました。

  • もはや input 要素の ref を提供する必要はありません。 DOM から入力要素を探し出し、見つからない場合は自動で挿入します。

  • カスタム input を制御された input として統合するために control.value を使用し、 control.change(value) を通じて値の状態を更新できるようになりました。フォームがリセットされると、値もリセットされます。

1import { useForm, useInputControl } from '@conform-to/react';
2import { CustomSelect } from './some-ui-library';
3
4function Example() {
5  const [form, fields] = useForm();
6  const control = useInputControl(fields.title);
7
8  return (
9    <CustomSelect
10      name={fields.title.name}
11      value={control.value}
12      onChange={(e) => control.change(e.target.value)}
13      onFocus={control.focus}
14      onBlur={control.blur}
15    />
16  );
17}