Reactを学び始めると、ほぼ必ず次の疑問にぶつかります。
- stateって何?
- propsとの違いは?
- 再レンダリングって何が起きてる?
- 配列やオブジェクトのstateはどう更新する?
- stateの持ち上げって何?
- props drillingってなぜ起きる?
- なぜ
prevを使う更新方法があるの?
この記事では、これらを 初心者でも理解できるように一つの流れで解説します。
1. stateとは何か?
Reactで state とは
コンポーネントが自分で持っている、変わるデータ
です。
例えば次のようなものは state になります。
- カウンターの数字
- 入力フォームの文字
- モーダルの開閉状態
- チェックボックスのON/OFF
- タブの選択状態
つまり
画面の中で変わるデータ
が state です。
2. propsとの違い
Reactには props というものもあります。
この2つは初心者がよく混乱します。
| props | state | |
|---|---|---|
| 誰が持つ | 親コンポーネント | 自分のコンポーネント |
| 変更 | 子は変更できない | 自分で変更できる |
| 用途 | 外から渡されるデータ | 内部状態 |
イメージ
props → 外からもらうデータ
state → 自分の状態
3. stateを作る方法
Reactでは useState を使います。
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>
+1
</button>
</div>
);
}
4. useStateを分解して理解する
この部分が一番重要です。
const [count, setCount] = useState(0);
意味はこうです。
count → 今の値
setCount → 更新する関数
useState → stateを作る
つまり
count = 状態
setCount = 状態を変えるボタン
5. なぜstateが必要なのか?
普通の変数ではダメなのでしょうか?
function Counter() {
let count = 0;
function handleClick() {
count = count + 1;
}
return (
<div>
<p>{count}</p>
<button onClick={handleClick}>+1</button>
</div>
);
}
このコードでは 画面は更新されません。
理由
Reactは変数の変更を監視していない
Reactに
状態が変わった
↓
UIを更新して
と伝える仕組みが state です。
6. 再レンダリングとは?
stateが変わると
コンポーネント関数がもう一度実行される
これを
再レンダリング
と呼びます。
流れ
state変更
↓
コンポーネント再実行
↓
新しいUI作成
↓
DOM更新
7. ReactはDOMを全部作り直すの?
違います。
Reactはこうします。
1 新しいUIを作る
2 前のUIと比較
3 違う部分だけ更新
この仕組みが
仮想DOM
です。
8. 配列のstate更新
初心者がよくやるミス。
❌ ダメな例
items.push("apple")
setItems(items)
これは
元の配列を直接変更している
ため良くありません。
✅ 正しい書き方
setItems([...items, "apple"])
意味
古い配列 + 新しい要素
↓
新しい配列を作る
9. オブジェクトのstate更新
オブジェクトも同じです。
❌ ダメ
user.name = "Tom"
✅ 正しい
setUser({
...user,
name: "Tom"
})
ポイント
新しいオブジェクトを作る
10. なぜ prev を使う更新方法があるのか?
Reactでは次のコードは 期待通りに動かない場合があります。
setCount(count + 1)
setCount(count + 1)
初心者はこう思います。
0 + 1
1 + 1
↓
2
でも実際は 1 になります。
11. なぜそうなるのか?
Reactはパフォーマンスのために
state更新をまとめて処理
します。
これを
Batching(バッチ処理)
と言います。
つまりこの時
count = 0
のまま2回計算されます。
0 + 1
0 + 1
結果
1
になります。
12. これを解決するのが prev
安全な書き方
setCount(prev => prev + 1)
prev は
直前のstate
です。
13. 同じコードをprevで書く
setCount(prev => prev + 1)
setCount(prev => prev + 1)
すると
0 → 1
1 → 2
になります。
14. オブジェクトstateでもprevを使う
setPerson(prev => ({
...prev,
name: e.target.value
}))
意味
prev = 最新state
↓
コピー
↓
nameだけ変更
15. stateの持ち上げ(Lifting State Up)
Reactではこんな問題が出ます。
子コンポーネント同士でデータを共有したい
例
Input
Preview
入力内容をプレビューしたい。
解決方法
stateを親に置きます。
App
├ Input
└ Preview
function App() {
const [text, setText] = useState("");
return (
<>
<Input text={text} setText={setText}/>
<Preview text={text}/>
</>
);
}
これを
stateの持ち上げ
と呼びます。
16. props drillingとは?
stateを上に持ち上げると起きる問題。
App
└ A
└ B
└ C
Cがstateを使う場合
App → A → B → C
とpropsを渡し続ける必要があります。
これを
props drilling
と言います。
17. props drillingの問題
問題はこれです。
関係ないコンポーネントまでpropsを渡す
例
App
└ Layout
└ Sidebar
└ Button
Buttonしか使わないのに
App → Layout → Sidebar → Button
と渡します。
18. 解決方法
大きいアプリでは
- Context API
- Zustand
- Redux
などを使います。
ただし
小さいアプリでは不要
なことも多いです。
まとめ
React初心者がまず理解すべきポイント。
state
コンポーネントが持つ変わるデータ
再レンダリング
コンポーネント関数の再実行
state更新
直接変更しない
新しい値を作る
prev更新
最新stateを安全に使う方法
stateの持ち上げ
共有stateは親に置く
props drilling
propsを深く渡し続ける問題
最後に
Reactの考え方はとてもシンプルです。
UI = f(state)
つまり
stateが変わる
↓
UIを再計算する
これだけです。
この考え方を理解すると
- フォーム
- Todoアプリ
- モーダル
- タブUI
- フィルター
などが一気に作りやすくなります。