NextJS icon

formライブラリのConformとshadcn-uiのSelectコンポーネントで動作不良が起きたときの原因と対策

公開日: 2025年05月14日カテゴリ: NextJS
#nextjs#react#error

発生した問題

conformのuseFormを用いてフォームを管理する際、セレクトコンポーネント(Shadcn UI/Radix UIベース)の選択状態が正しく反映されない問題が発生した。具体的には次のような状況:

  1. セレクトボックスで値を選択する
  2. 別の値を選択しようとする
  3. UIには最初に選択した値が表示されたままになる

原因

調べてみた感じ、以下の問題っぽかった。

  1. イベント発火の問題
    • Conformはネイティブフォームイベント(input、change、blur)を監視して状態を管理している
    • カスタムUIコンポーネントはこれらのイベントを自動的に発火しない

解決策

(例):

export const ExampleSelect: React.FC<ExampleSelectProps> = ({
  options,
  placeholder = "選択してください",
  label,
  meta,
}) => {
  const control = useInputControl(meta);
  const id = useId();

  const handleValueChange = (selectedValue: string) => {
    if (selectedValue === "_blank" || selectedValue === "") {
      control.change(undefined);
    } else {
      control.change(selectedValue);
    }
    // フォーカスとブラーイベントを明示的に発火
    control.focus();
    control.blur();
  };

  // control.valueを使用して現在の値を取得
  const currentValue = control.value ?? "";

  return (
    <div className="flex items-center gap-2">
      {/* ... */}
      <Select
        name={meta.name}
        value={currentValue}
        onValueChange={handleValueChange}
      >
        {/* ... */}
      </Select>
    </div>
  );
};

重要なポイント

  1. useInputControlフックの活用

    • Conformが提供するuseInputControlフックを使用して入力制御を行う
    • control.valueを参照することで常に最新の値を取得
  2. イベントの明示的な発火

    • control.change():値を更新し、changeとinputイベントを発火
    • control.focus():focusとfocusinイベントを発火
    • control.blur():blurとfocusoutイベントを発火
  3. イベントの順序

    • 値の変更後にfocus()blur()の順で呼び出すことで、Conformに状態変更を確実に通知

なぜこれで解決したのか

shadcn-uiはネイティブHTML要素とは異なり、ブラウザの標準的なフォームイベントを自動的にしないみたいだ。

Conformはこれらのイベントを監視して状態を管理しているため、明示的にイベントを発火させる必要がある。

control.focus()control.blur()を連続して呼び出すことで、Conformに「このフィールドにフォーカスが当たり、その後フォーカスが外れた」ことを通知する。これにより、Conformは内部状態を更新し、UIに反映させることができる。

参考資料

まとめ

shadcn-uiとConformを統合する際は、以下の点に注意する必要がありそうだ:

  1. useInputControlフックを使用して入力制御を行う
  2. 値の変更時にイベントを明示的に発火させる
  3. 値の参照にはcontrol.valueを使用する