React 〜制御・非制御コンポーネント〜
自作サービスの開発でフォームを使用したのですが、自分が実装したコンポーネントが制御か非制御かを全く理解せずに開発をしていたので、少しまとめてみようと思います。
一応公式でも説明されていますが内容が古く、コーディング例もクラスコンポーネントで書かれているので正直分かりづらいです。
制御 vs 非制御コンポーネント
この2つはReactにおいてフォームを扱うコンポーネントの分類。
制御コンポーネント
useState、onChangeを使用してフォームの入力値をstateで管理する。
const ControlledForm: FC = () => { const [inputValue, setInputValue] = useState<string>(''); const handleChange = (e: ChangeEvent<HTMLInputElement>) => setInputValue(e.target.value); const handleSubmit = (e: FormEvent<HTMLFormElement>) => { e.preventDefault(); console.log(inputValue); setInputValue = ''; }; return ( <form onSubmit={handleSubmit}> <input type="text" value={inputValue} onChange={handleChange} /> <input type="submit" value="確定" /> </form> ); };
メリット
- 入力に応じたバリデーションを設定するなど、リアクティブな実装が可能
デメリット
- 入力値が変更される度に再レンダリングが発生するのでパフォーマンスが落ちる
- 記述が多くなりがち
Input、Formのそれぞれ、引数のe
に型を定義しないといけないのが少し手間で直感的に分かりづらいと思いました。
ですが、Reactに入門するとまずはこの方法でフォームの状態管理を説明しているものが多いと思います。
非制御コンポーネント
フォームの入力値をDOMで操作する。
onSubmit関数に型ComponentProps<"form">["onSubmit"]
を指定することで引数eの型定義が不要になり、また、eからフォームの入力値へアクセス可能。
const UncontrolledForm: FC = () => { const handleSubmit: ComponentProps<"form">["onSubmit"] = (e) => { e.preventDefault(); // 入力された値を取得 console.log(e.currentTarget.text.value); e.currentTarget.reset() }; return ( <form onSubmit={handleSubmit}> <input type="text" name="text" /> <input type="submit" value="確定" /> </form> ); }
メリット
- 記述量がグッと減る
- 再レンダリングが発生しないので動きが軽くなる
デメリット
- リアクティブなバリデーションが行えない
submitされないと入力値の参照ができないため、submitの前にバリデーションをかけることや、バリデーションにかかった場合にボタンを非活性にするといった処理が行えないのが少々痛い。
react-hook-form
フォームのバリデーションを簡単に実装できるnpm。 非制御コンポーネントが用いられているがリアクティブにバリデーションを行うことも可能。
制御・非制御コンポーネントかを意識せずに使用できるのでおすすめ。
まとめ
Reactの学習が不十分だったため、サービス開発中は制御コンポーネント、非制御コンポーネントという言葉すら知りませんでした...。
そしてreact-hook-form
を使用していたので全く意識せずとも実装ができてしまっていました。
とりあえず動くものを作れるようになったので次はちゃんとReactを書けるようになりたい。 基礎概念の理解に努めようと思います。