React で input の defaultValue の変更を反映させる

React で input の defaultValue の変更を反映させる

React.js の フォーム要素には 制御コンポーネント (controlled component)非制御コンポーネント (uncontrolled component) があります。フォーム要素とは inputtextarea などを指します。

今回は非制御コンポーネントで初期値 (defaultValue) を後から変更する方法を紹介します。

非制御コンポーネントについて

ざっくり説明すれば、制御コンポーネントと非制御コンポーネントは下記のような違いがあります。

  • 制御コンポーネント: valueonChange をバインドする ➡ React で値を管理する
  • 非制御コンポーネント: defaultValueref をバインドする ➡ DOM で値を管理する

React のメリットを活かすために、通常は制御コンポーネントを使えばいいのですが、フォームでまとめて値を扱いたい場合など、非制御コンポーネントのほうが扱いやすい場合もあります。

非制御コンポーネントの初期値について

非制御コンポーネントの場合、最初に表示しておく値として、以下のように defaultValue を指定します。

React非制御コンポーネント
<input defaultValue={initialValue} />

公式の説明にもあるとおり、この defaultValueコンポーネントのマウント時のみ設定され、その後、仮に値を変更してもフォーム要素の値が書き換わることはありません。

コンポーネントのマウント後に defaultValue 属性の値を変更しても DOM 内の値の更新は引き起こされません。 デフォルト値 - 非制御コンポーネント – React

試しに下記の CodePen を開き、 [change] ボタンを押してみてください。 state 自体は hogefuga が入れ替わっているのですが、 input に表示される文字は変化しないはずです。

通常はこの動きで問題ないのですが、まれに後から初期値を書き換えたいことがあります。たとえば、 useEffect で API から取ってきたデータを初期値として表示したい場合などです。

key を指定して初期値の変更を反映する

結論から言うと、初期値の変更をフォーム要素に反映するには key 属性を指定します。 key 属性に渡す値は初期値、つまり defaultValue と同じ値です。

key属性を指定したReact非制御コンポーネント
<input key={initialValue} defaultValue={initialValue} />

これにより、初期値が変更されたときはフォーム要素が再描画され、 defaultValue の値が表示されるというわけです。

試しに、さきほどの CodePen の 5 行目でコメントアウトされている部分を解除してみてください。

input要素にkey属性を指定
const DemoComponent = (props) => {
  const { firstValue } = props;
  return (
    <input
      key={firstValue} // uncomment me to re-render on firstValue changed      defaultValue={firstValue}
    />
  );
};

const App = () => {
  const [initValue, setInitValue] = React.useState("hoge");
  const handleClick = () =>
    setInitValue(initValue === "hoge" ? "fuga" : "hoge");
  return (
    <div>
      <div>
        <button onClick={handleClick}>change</button>
        {" state: "}
        {initValue}
      </div>
      <div>
        <DemoComponent firstValue={initValue} />
      </div>
    </div>
  );
};

ReactDOM.render(<App />, document.querySelector("#app"));

おそらく [change] ボタンで input に表示される内容が切り替わると思います。

key 属性は配列の要素を map するときによく指定しますが、このような使い方も可能です。

当たり前ですが、初期値を入れ替えて再描画するということはユーザーが入力中の内容は破棄されますので、値が書き換わるタイミングに配慮しておかなければなりません。

kenzauros