はじめに
React の useState
や useReducer
などのState管理には、いくつかの制約が存在します。
これらの制約を破るとエラーが発生し、意図しない動作の原因となります。
私自身も実装中に制約違反でエラーを発生させてしまった為、代表的なStateの制約違反とそのエラーについて、纏めておきたいと思います。
1. useState
の制約違反
1-1. State を直接変更してしまう
エラー内容
const [count, setCount] = useState(0);
// 直接変更しようとするとエラーの原因に
count = count + 1;
エラー原因
-
useState
で管理されている変数を直接変更すると、React が状態変更を検知できず、意図しない動作につながる。
正しい実装方法
setCount(count + 1);
または、以下のように関数で実装する
setCount(prevCount => prevCount + 1);
1-2. useState
の更新を同期的に期待する
エラー内容
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
console.log(count); // 期待通りの値にならない
};
エラー原因
-
useState
の更新は非同期で行われるため、setState
を実行しても即座には反映されない。
正しい実装方法
const handleClick = () => {
setCount(prevCount => prevCount + 1);
console.log(count); // 前の値が表示されるが、Reactの更新後には期待通りに反映される
};
2. useEffect
の制約違反
2-1. useEffect
の中で setState
を適切に管理しない
エラー内容
const [data, setData] = useState(null);
useEffect(() => {
fetch("/api/data")
.then(res => res.json())
.then(json => setData(json)); // `setData` が無限ループを引き起こす可能性あり
}, [data]); // 依存配列が間違っている
エラー原因
-
data
をuseEffect
の依存配列に含めると、setData(json)
のたびにuseEffect
が再実行されてしまい、無限ループになる。
正しい実装方法
useEffect(() => {
fetch("/api/data")
.then(res => res.json())
.then(json => setData(json));
}, []); // 初回のみ実行されるように修正
3. useReducer
の制約違反
3-1. Reducer 内で State を直接変更してしまう
エラー内容
const reducer = (state, action) => {
state.count += 1; // 直接変更
return state;
};
エラー原因
-
useReducer
のstate
は 不変(immutable) であるべき。- 直接変更すると、React が変更を検知できず、更新が反映されない。
正しい実装方法
const reducer = (state, action) => {
return { ...state, count: state.count + 1 }; // 新しいオブジェクトを作成
};
4. useRef
の制約違反
4-1. useRef
を State のように使おうとする
エラー内容
const countRef = useRef(0);
const handleClick = () => {
countRef.current += 1;
console.log(countRef.current); // 値は更新されるが、コンポーネントの再レンダリングが発生しない
};
問題点
-
useRef
の値は変更できるが、React のレンダリングをトリガーしない。 -
useState
と違い、値が変更されても UI には反映されない。
正しい実装方法
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(prevCount => prevCount + 1); // 正しく UI を更新
};
5. useEffect
内で setState
を誤ったタイミングで実行
5-1. useEffect
内で setState
を条件なしで呼び出す
エラー内容
const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1); // 毎回 `useEffect` が発火 → 無限ループ
}, [count]);
エラー原因
-
setCount(count + 1)
をuseEffect
内で呼ぶと、count
の変更によってuseEffect
が再実行され、無限ループが発生する。
正しい実装方法
useEffect(() => {
if (count < 5) { // 条件を追加
setCount(count + 1);
}
}, [count]);
まとめ
制約違反 | 原因 | 解決策 |
---|---|---|
State を直接変更 | React が変更を検知できない |
setState を使って更新 |
State の更新を同期的に期待 |
useState は非同期で動作する |
setState(prev => prev + 1) を使う |
useEffect の依存配列ミス |
無限ループの原因になる | 依存配列を適切に設定 |
Reducer で State を直接変更 | React の不変性ルール違反 | return { ...state, newValue } |
useRef を State のように使用 |
useRef はレンダリングをトリガーしない |
useState を使う |
useEffect 内で setState を条件なしで実行 |
無限ループになる | 条件付きで setState を呼ぶ |
さいごに
ダイエットが順調に進行中です、減量目標まで50%達成・・・!
健康管理も実装も全力で対応していきますー!