🙌

【超初心者向け】ReactのuseStateを「単語ノート」で理解する

に公開

ReactのuseStateやmapって、最初すごく難しく感じませんか?

「なんで配列をこうやって書き換えるの?」
「mapって何をしてるの?」

この記事では、単語帳アプリを作りながら、
useStateの仕組みを“超ていねい”に解説します。

完成イメージ

作るのはこんなシンプルなアプリ👇

  • 単語を入力して「追加」できる
  • 追加した単語を一覧で表示
  • 「削除」ボタンで単語を消せる

コード全体

tsx
"use client";

import { useState } from "react";

export default function Home() {
  const [words, setWords] = useState<string[]>([]);
  const [newWord, setNewWord] = useState("");

  const addWord = () => {
    // 入力チェック
    if (newWord.trim() === "") return;  
    // 入力された単語をリストに追加
    setWords([...words, newWord]);      
    // 入力欄を空にする
    setNewWord("");                    
  };

  const deleteWord = (index: number) => {
    setWords(words.filter((_, i) => i !== index));
  };

  return (
    <main className="p-8 max-w-md mx-auto">
      <h1 className="text-2xl font-bold mb-4">単語リスト</h1>

      <div className="flex gap-2 mb-4">
        <input
          type="text"
          value={newWord}
          onChange={(e) => setNewWord(e.target.value)}
          placeholder="英単語を入力"
          className="border p-2 flex-1 rounded"
        />
        <button
          onClick={addWord}
          className="bg-blue-500 text-white px-4 py-2 rounded"
        >
          追加
        </button>
      </div>

      <ul className="space-y-2">
        {words.map((word, index) => (
          <li
            key={index}
            className="flex justify-between items-center border p-2 rounded"
          >
            <span>{word}</span>
            <button
              onClick={() => deleteWord(index)}
              className="text-red-500 hover:underline"
            >
              削除
            </button>
          </li>
        ))}
      </ul>
    </main>
  );
}

useStateとは?

tsx
const [words, setWords] = useState<string[]>([]);

ここでは:
*words:現在の単語リスト
*setWords:wordsを更新するための関数
*useState<string[]>([]):初期値は空の配列 [](型は文字列の配列)
*stringは文字列のこと
つまり、ノート(words)と鉛筆(setWords)を渡されるようなものです。
新しい単語を追加したいときは「鉛筆(setWords)」を使ってノートに書き足します。

入力フォームの仕組み

tsx
const [newWord, setNewWord] = useState("");

ここでは「今入力中の文字」をnewWordに保存しています。
*newWord:入力中の文字(例:ユーザーが"apple"と入力中なら"apple"
*setNewWord:入力内容が変わるたびに更新する関数
つまり...
「入力中の単語」と「保存済みの単語リスト」を分けて管理している。

onChangeでリアルタイムに更新↓

tsx
<input
  type="text"
  value={newWord}
  onChange={(e) => setNewWord(e.target.value)}
/>

*e.target.valueで「今入力した文字」を取得
*それをsetNewWord()に渡すことで、画面上のstateを更新。
つまり...
「入力欄の文字」と「Reactの中のデータ」が常にリアルタイムで同期してる

単語を追加する

tsx
const addWord = () => {
  if (newWord.trim() === "") return; // 空欄防止
  setWords([...words, newWord]); // 既存配列+新単語
  setNewWord(""); // 入力欄をリセット
};

ここが一番のポイント!
setWords([...words, newWord]) は「今ある単語の配列(words)」に「新しい単語」を追加した新しい配列を作って、再描画させています。

Reactは直接 words.push() のように配列を変更してはいけません。
理由は、「stateの変更をReactが検知できない」からです。

だから 新しい配列を作って渡す のが正解。

  • もっと具体的に説明すると...
if (newWord.trim() === "") return;

入力欄が空なら何もしない。
1.trimとは文字列の前後の空白を削除するメソッドのこと。
2.===厳密等価演算子(きびつとうかえんざんし)といい、「左右の値が中身も型も完全に同じかどうか」をチェックします。

setWords([...words, newWord]);

1....スプレッド構文と言って、配列やオブジェクトの「中身を展開する」書き方。
例えば

const words = ["apple", "banana"];
const newWords = [...words, "cherry"];

console.log(newWords);
// → ["apple", "banana", "cherry"]

この場合
*words はもともとの配列
*...words はその中身を展開
*newWord は新しく追加したい単語
だから

setWords([...words, newWord]);

は、「今ある単語リスト(words)の末尾に、新しい単語(newWord)を追加して、新しい配列を作る」
という意味です。

削除ボタンの仕組み

const deleteWord = (index: number) => {
    setWords(words.filter((_, i) => i !== index));
  };

const deleteWord = (index: number) => { ... }
意味:
「削除ボタン」を押したときの処理。
どの単語を消すかを「index(番号)」で判断している。

setWord(word.filter((_, i) => i !== index));

これがちょっと難しそうに見えるけど、
「配列 words の中から、指定した番号(index)の要素を削除する」
という処理をしています。
1.filterとは?
filter()配列の中から「条件にあうものだけ」を取り出すメソッドのこと。
例えば:

const numbers = [1, 2, 3, 4, 5];

// 偶数だけを取り出す
const even = numbers.filter(n => n % 2 === 0);

console.log(even);
// → [2, 4]

ポイント:
*filter() は 元の配列を変更せずに 新しい配列を返す。
*条件が true になった要素だけが残る。
2.( _, i ) => i !== indexの意味
filter()の中の関数には、通常2つの引数を渡せる。

array.filter((要素, インデックス) => 条件)

この場合:
*_は要素そのもの(ここでは使わないから _ としてる)
*iは配列のインデックス(0, 1, 2, ...)
i !== index
「今見ている要素のインデックスが削除したい番号じゃないなら残す」
という意味です。
つまり

setWords(words.filter((_, i) => i !== index));

を日本語で言うと、
「今の words 配列から、削除したいインデックスの要素を取り除いた新しい配列を作って、それを state にセットする」と言う意味になる。

入力フォーム部分

<div className="flex gap-2 mb-4">
  <input
    type="text"
    value={newWord}
    onChange={(e) => setNewWord(e.target.value)}
    placeholder="英単語を入力"
    className="border p-2 flex-1 rounded"
  />
  <button
    onClick={addWord}
    className="bg-blue-500 text-white px-4 py-2 rounded"
  >
    追加
  </button>
</div>

*onChange={(e) => setNewWord(e.target.value)}
→入力が変わるたびにsetNewWord()newWordの値を更新する。
例えば「apple」と入力すると、newWordの中身もappleに変わる。

単語一覧部分

<ul className="space-y-2">
  {words.map((word, index) => (
    <li
      key={index}
      className="flex justify-between items-center border p-2 rounded"
    >
      <span>{word}</span>
      <button
        onClick={() => deleteWord(index)}
        className="text-red-500 hover:underline"
      >
        削除
      </button>
    </li>
  ))}
</ul>

ここでやっていること

*word(配列)を.map()でループして、1つずつ<li>要素にして表示。
*各行には「単語」と「削除ボタン」を表示。

<li key={index}>

keyはReactがリストをレンダリングするための識別子。
ここではインデックス番号を使っている。

まとめ

今回はReactのuseStateについて紹介しました。
実際に手を動かしてみると、仕組みの理解が深まると思います。
少しでも参考になれば嬉しいです。
ここまで読んでいただき、ありがとうございました!

Discussion