コードが書けない高専卒がUdemyのコードをアップデートしてみた

# はじめに
やっとこさ【最新ver対応済】モダンJavaScriptの基礎から始める挫折しないためのReact入門を修了しました！
ハンズオン形式でTodoリストを作成していくコースです。
最初はバニラJSで作りその後Reactで再度作成したのですが、Reactが便利すぎてすでにバニラJSでの書き方8割忘れました笑

作成したTodoリストはこちら👇
テキストボックスでTodoを入力して、未完了エリアと完了エリアに分け可視化しています。
![スクリーンショット 2024-05-08 23.54.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3781911/fdff07bd-a958-ffe2-4786-0754e344f8a8.png)


# 本題
コースで作成したTodoリストですが「タスクの一意性をテキストで担保しているため、同一のタスクが入力された際に意図しない挙動を行う」といういじくりがいを秘めています（！）
好奇心がうずいてしまったため、寄り道してこちらを解決していきます！！

### 原因

下記のコードにてリストのkeyを設定していますが、タスクのテキスト(todo)にて設定しているため、同一文章のタスクを設定されたときに意図しない挙動が発生するようになります。
```React:IncompleteTodos.jsx
<li key={todo}>
    <div className="list-row">
      <p className="todo-item">{todo}</p>
      <button onClick={() => onClickBack(index)}>戻す</button>
    </div>
</li>
```


# 解決案を考える
この問題を解決するため、タスクごとに固有のIDを振ることで一意性を担保しようと考えました。
ChatGPT先生と相談してみたところ、以下の解決案を提示してもらえました。
### 解決案① useIdを活用する
パッと見それっぽいです。ただ追加ボタンを押されたイベントハンドラーの中では使用できないため不可と判断しました。

### 解決案② uuidを振る
実際にuuidライブラリを活用して生成してみました。
実装しているときは、このuuidを持ったオブジェクトをどうやって見つけるかがわからず断念しました。
ただ後述の③のやり方とアルゴリズムは同じなためこちらでも実装可能だと思います。

### 解決策③ useStateでidを定義する
今回はこの方法で実装しました。
useStateでの値の扱い方は講座でも習ったため、復習としてちょうどよかったと思います。

# 実際のコード(一部抜粋)
コード全体は[こちら](https://stackblitz.com/edit/vitejs-vite-11ubo9?file=src%2FTodo.jsx)
```react:Todo.jsx
export const Todo = () => {
  const [id, setId] = useState(1);
  //idをインクリメントする関数を定義
  const incrementIndex = () => setId((prevId) => prevId + 1);

    const onClickAdd = () => {
        if (todoText === '') return;
        //idと入力されたテキストを持つsetIdTodosを作成
        const setIdTodos = {
          id: id,
          text: todoText,
        };
        //未完了リストへタスクを挿入
        const newTodos = [...incompleteTodos, setIdTodos];
        setIncompleteTodos(newTodos);
        setTodoText('');
        incrementId();
      };
};

//完了ボタンが押されたとき
  const onClickComplete = (id) => {
    const newIncompleteTodos = [...incompleteTodos];
    //incompleteTodosの中身は [{id:1, text:"hoge"},{id:2, text:"fuga"}]
    //完了ボタンが押されたタスクのidに一致するオブジェクトを取り出す
    const target = incompleteTodos.findIndex(({ id }) => id === id);
    //そのオブジェクトを未完了エリアから削除する
    newIncompleteTodos.splice(target, 1);
    //オブジェクトを完了エリアに挿入する
    const newCompleteTodos = [...completeTodos, incompleteTodos[target]];

    setIncompleteTodos(newIncompleteTodos);
    setCompleteTodos(newCompleteTodos);
  };
```

```react:IncompleteTodos.jsx
  <li key={todo.id}>
    <div className="list-row">
      <p className="todo-item">{todo.text}</p>
      <button onClick={() => onClickComplete(todo.id)}>完了</button>
      <button onClick={() => onClickDelete(todo.id)}>削除</button>
      <p>{todo.id}</p>
    </div>
  </li>
```

# 結果
実装できました！
Udemyは作れた気になっちゃいがちなので、手だけではなくてこうして頭も使って実力をつけていきたいと思います💪
ChatGPT先生にもご協力いただきながら、久しぶりにプログラミングした感じがしてとても楽しかったです！

## 参考文献
+ [ReactでuseIdを使い一意な文字列を生成し汎用コンポーネントのid重複を防ぐ](https://weseek.co.jp/tech/4202/)
+ [【React】そろそろ技術ブログで setCount(count + 1) と書くのはやめませんか](https://zenn.dev/stin/articles/use-appropriate-api)
+ [.indexOf()で配列のindexが取得できないとき](https://qiita.com/risagon/items/a885704a816540a9e58e)
+ [Array.prototype.findIndex()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex)