「フォームのクリアをしたのに、チェックボックスだけリセットされない！」問題の解決法✨

## はじめに

「リセットボタンを押したのに、なぜかチェックボックスだけ元の状態のままで変わらない…」

こんな経験はありませんか？Reactでフォームを実装していると、一見すべてのフォーム要素をリセットしたはずなのに、なぜかチェックボックスだけが頑固に元の状態を維持している…という状況に遭遇することがあります。

今回は、この「チェックボックスだけリセットされない問題」の原因と、シンプルな解決策について解説します。その鍵は、`defaultChecked`と`checked`プロパティの違いにあります。

## 問題の再現：チェックボックスだけがリセットされない...

まず、問題が発生するシンプルな例を見てみましょう

```jsx
import React, { useState } from 'react';

function ProblemForm() {
  const [name, setName] = useState('');
  const [isAgree, setIsAgree] = useState(false);

  const handleReset = () => {
    setName('');
    setIsAgree(false);
    // ここでフォームをリセットしているつもり...
  };

  return (
    <form>
      <div>
        <label>名前:</label>
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
        />
      </div>
      
      <div>
        <label>
          <input
            type="checkbox"
            defaultChecked={isAgree}
            onChange={(e) => setIsAgree(e.target.checked)}
          /> 利用規約に同意する
        </label>
      </div>
      
      <button type="button" onClick={handleReset}>
        リセット
      </button>
    </form>
  );
}
```

このコードでは、リセットボタンをクリックすると次のことが起きます：

1. `setName('')`で名前フィールドは空になります（UIも更新される）
2. `setIsAgree(false)`でチェックボックスの状態は内部的に`false`になります
3. しかし、チェックボックスのUI上の表示は変わりません！

これは`defaultChecked`プロパティの性質によるものです。

## defaultCheckedとcheckedの本質的な違い

### defaultChecked

`defaultChecked`は、その名前が示す通り「デフォルト（初期）値のみを設定する」ためのプロパティです。

- コンポーネントが**最初にレンダリングされるときだけ**適用される
- 一度レンダリングされたら、ステートが変わっても再評価されない
- HTMLの`defaultChecked`と同様の挙動をする

### checked

一方、`checked`は「現在の値」を表すプロパティです。

- 常に**現在の状態**を反映する
- 値が変わるたびに再評価され、UIも更新される
- ReactによるUIの制御を可能にする

## 解決策：defaultCheckedからcheckedへの変更

問題を解決するには、`defaultChecked`を`checked`に変更するだけです：

```jsx
import React, { useState } from 'react';

function FixedForm() {
  const [name, setName] = useState('');
  const [isAgree, setIsAgree] = useState(false);

  const handleReset = () => {
    setName('');
    setIsAgree(false);
  };

  return (
    <form>
      <div>
        <label>名前:</label>
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
        />
      </div>
      
      <div>
        <label>
          <input
            type="checkbox"
            checked={isAgree}  {/* defaultCheckedからcheckedに変更 */}
            onChange={(e) => setIsAgree(e.target.checked)}
          /> 利用規約に同意する
        </label>
      </div>
      
      <button type="button" onClick={handleReset}>
        リセット
      </button>
    </form>
  );
}
```

この変更により：

1. `setIsAgree(false)`が実行されると、`isAgree`ステートが`false`に更新される
2. `checked={isAgree}`は新しい値`false`を反映する
3. チェックボックスのUI状態も正しく更新される

## いつdefaultCheckedを使い、いつcheckedを使うべきか？

### defaultCheckedの使いどころ

- フォームのリセットやプログラムによる変更が**不要な場合**
- ユーザー入力のみでチェックボックスを制御する場合
- 初期値のみを設定して、あとはユーザーに委ねる場合

### checkedの使いどころ

- フォームのリセット機能がある場合（今回のケース）
- プログラムによる状態変更が必要な場合
- 他のUI要素と連動して状態を変更する場合

## チェックボックスのリセット問題をチェックするポイント

フォームのリセット機能を実装する際は、以下のポイントを確認しましょう：

1. チェックボックスには`defaultChecked`ではなく`checked`を使用しているか
2. ステートの更新とUI表示が連動しているか
3. 全てのフォーム要素で統一した制御方法（制御コンポーネント）を使用しているか

## まとめ

Reactでフォームを実装する際、チェックボックスだけがリセットされない問題は`defaultChecked`と`checked`の違いを理解することで簡単に解決できます。

- `defaultChecked`：初期レンダリング時のみ評価される
- `checked`：常に現在の値を反映し、値が変わるとUIも更新される

プログラムによってチェックボックスの状態を変更する必要がある場合（フォームリセットなど）は、必ず`checked`プロパティを使用しましょう。これにより、内部状態とUI表示の同期が保たれ、ユーザーに一貫した体験を提供できます。

次回フォームを実装するときは、チェックボックスに`defaultChecked`と`checked`のどちらを使うべきか、意識して選択してみてください。