📚

controlled input と uncontrolled input

2022/03/04に公開

今回の経緯について

会員登録ページにて処理を実装していたところ、こんなエラーが出現しました。
ここでuncontrolledとcontrolledという言葉が出てきたので、調べてみました。

目次

  • controlled
  • uncontrolled
  • まとめ

controlled

controlledとは、直訳すると管理されたという意味があります。

Reactでは、controlledとuncontrolled二つのタイプのinputを作成することが出来ます。

これが、条件分岐などで予期せず入れ替わってしまい、先ほどのエラーが起きてしまうようです。

ここからが本題で、まずcontrolled inputがどういうものなのか、コード等を交えてご紹介します。

controlled inputでフォーム作成時のコード

import { SetStateAction, useState } from "react";

const Form = () => {
    const [state, setState] = useState<string>("");

    const handleStateChange = (e: { currentTarget: { value: SetStateAction<string>; }; }) => {
        console.log(e.currentTarget.value);
        setState(e.currentTarget.value);
    };

    return(
        <div>
            <label>state</label>
            <input 
                type="text" 
                value={state}
                onChange={handleStateChange}
            />
        </div>
    )
}

export default Form;

controlled inputの特徴

実際に動かしてみたとき、inputの値は常に管理・監視されています。
handleStateChangeで出力されたログを見るとこんな感じ。

これがcontrolledの1つ目の特徴になります。

これを使って1入力毎にバリデーションをすることもできるようになります。

2つめはformを使って書く必要がない点です。

通常、formの要素を送り、そこから必要な情報を取り出して使用しますが、
controlledではその必要がない(inputを監視し、入力ごとにデータを取得している)為、form要素が無くても上記のように動作します。

useState

controlled inputを作成するにあたり、今回はReact hooksのuseStateを使用しました。
一応ご紹介しますが、既に知っている方は飛ばしてしまって問題ないです。

const [state, setState] = useState<Type>("");

※typeScriptでの例なので、型の設定がされていますが、Reactのみを使用されている場合は型の設定は要りません。

useStateの第一引数として、stateの初期値を設定しますが、
初期値として何も入れないとエラーが起きてしまうのでご注意ください。

//エラー発生
const [state, setState] = useState<string>();

そして、setStateはメソッドであり、これを使用することで、stateの値を変更します。

setState("something")// state="something"

uncontrolled

uncontrolledは先ほど少し話に出しましたが、従来のformを使って値を送る方法です。

React hooksでは、useRefを用いてこれを再現します。

import { useRef } from "react";

const Form = () => {
    const ref = useRef<HTMLInputElement>(null)

    const submitHandler = (e: React.ChangeEvent) => {
        e.preventDefault()
        console.log(ref.current?.value)
    };

    return(
        <form onSubmit={submitHandler}>
            <input type="text" ref={ref}/>
            <button type="submit">ボタン</button>
        </form>
    );
}

export default Form;

ボタン押下時に動作し、ログにはこのように出ます。

普段React以外を使って開発される方はよく見る機会のある形かと思います。
ボタンを押下し、formが送られる以外に管理・監視しないので、uncontrolledと呼ばれるわけですね。

typeScriptを使う上での注意点

submitHandlerでは下記のように「?」を使ってます。

console.log(ref.current?.value)

これは「null」を許容する為に使用し、付けないと「オブジェクトがnullになる可能性があります。」というようなエラーが出現しますので、ご注意ください。

「?」はifみたいなものだと思ってください。

useRef

uncontrolled inputを作成するにあたり、今回はReact hooksのuseRefを使用しました。
こちらも一応ご紹介しますが、既に知っている方は飛ばしてしまって問題ないです。

const ref = useRef<Type>(null)

typeScriptを使用する場合は上記のように書きますが、Reactのみの場合は、下記のように書きます。

const ref = useRef()

そして、どのようにして値を取得するかですが、inputのrefプロパティにセットしてあげましょう。

<input type="text" ref={ref}/>

あとは、formを何かしらのアクション発生時に送信してあげれば、先ほどのように取得出来るといった流れになります。

typeScriptでuseRefを使用時の型についてのエラーを下記で説明しております。良ければご覧ください。
https://zenn.dev/tns_00/articles/react-typescript-form-error-of-value

まとめ

最後にcontrolledとuncontrolledについてまとめます。

controlledとは管理された状態。つまり、変化がある度に取得します。

uncontrolledとは、管理されていない状態で、結果だけを取得します。

仕事をする上で、工程毎に評価するか、結果だけで評価するかみたいな違いですね。

工程毎に評価する事により、その工程上で問題があれば指摘出来たりするわけです。

また、controlledでは管理を別々にすることが出来るので、
1作業目最中に評価、2作業目最中に評価・・・
といったようにすることができます。

例を挙げると、メールとパスワードを入力するフォームがあったとします。
メールを打ってる際にメール側をチェック、パスワードを打つ際にパスワードをチェック
とすることにより、差し戻しを少なく出来ます。

ログイン時と登録時で使い分けるなど、この違いを何をするかで上手く使い分けていきたいですね。

ここまで読んで下さり、ありがとうございました。

参考にしたサイト

https://www.codingdeft.com/posts/react-controlled-uncontrolled/#uncontrolled-input

Discussion