【徹底解説】useStateで再レンダリングをトリガーする方法!イベントハンドラ内で状態更新しても再レンダリングされない問題を解決
React Hook useState が再レンダリングをトリガーしない理由
答え: useState
で状態を更新しても、その時点ですでに実行中のイベントハンドラ内では再レンダリングはトリガーされないためです。
React のレンダリング仕組み
React は以下のフローでレンダリングを行います。
- 状態の更新
- 仮想 DOM の生成 3.実際の DOM への反映
useState
で状態を更新すると、まず状態の更新処理が実行されます。しかし、その後に実行される仮想 DOM の生成と実際の DOM への反映処理は、現在のイベントハンドラが完了した後に実行されます。
イベントハンドラ内の状態更新
イベントハンドラ内で useState
を使って状態を更新した場合、その後の仮想 DOM 生成と実際の DOM への反映処理では 更新前の状態 が参照されます。
例:useState でカウントアップボタンを実装
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
console.log(count); // 0 と出力される
};
return (
<div>
<p>カウント:{count}</p>
<button onClick={handleClick}>カウントアップ</button>
</div>
);
このコードでは、ボタンクリック時に setCount
を使って count
の値を 1 増やしています。しかし、console.log
で出力される count
の値は 常に 0 です。
これは、setCount
が実行された時点ではまだイベントハンドラが完了していないため、仮想 DOM 生成と実際の DOM への反映処理では 更新前の状態 である 0
が参照されるからです。
再レンダリングをトリガーする方法
イベントハンドラ内で状態更新を行った後に再レンダリングをトリガーするには、以下の方法があります。
- useEffect Hook を使う
useEffect
Hook を使って、状態更新後に処理を実行することができます。
const [count, setCount] = useState(0);
useEffect(() => {
console.log(count); // 1 と出力される
}, [count]);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<p>カウント:{count}</p>
<button onClick={handleClick}>カウントアップ</button>
</div>
);
このコードでは、useEffect
Hook を使って、count
の値が更新された後に console.log
を実行しています。
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return state + 1;
default:
return state;
}
};
const [count, dispatch] = useReducer(reducer, 0);
const handleClick = () => {
dispatch({ type: 'increment' });
};
return (
<div>
<p>カウント:{count}</p>
<button onClick={handleClick}>カウントアップ</button>
</div>
);
このコードでは、useReducer
Hook を使って、count
の値を管理しています。
useState
は非常に便利な Hook ですが、その動作について理解していないと、思い通りに動作しないことがあります。イベントハンドラ内で状態更新を行う場合は、再レンダリングをトリガーする方法を理解しておくことが重要です。
useState でカウントアップボタンを実装
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
console.log(count); // 0 と出力される
};
return (
<div>
<p>カウント:{count}</p>
<button onClick={handleClick}>カウントアップ</button>
</div>
);
useEffect Hook を使って再レンダリングをトリガー
const [count, setCount] = useState(0);
useEffect(() => {
console.log(count); // 1 と出力される
}, [count]);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<p>カウント:{count}</p>
<button onClick={handleClick}>カウントアップ</button>
</div>
);
useReducer Hook を使って状態管理を行う
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return state + 1;
default:
return state;
}
};
const [count, dispatch] = useReducer(reducer, 0);
const handleClick = () => {
dispatch({ type: 'increment' });
};
return (
<div>
<p>カウント:{count}</p>
<button onClick={handleClick}>カウントアップ</button>
</div>
);
useState で状態更新後に再レンダリングをトリガーする他の方法
const [count, setCount] = useState(0);
const countRef = useRef(0);
const handleClick = () => {
setCount(count + 1);
countRef.current = count;
};
return (
<div>
<p>カウント:{countRef.current}</p>
<button onClick={handleClick}>カウントアップ</button>
</div>
);
このコードでは、useRef
Hook を使って countRef
という変数を生成しています。countRef.current
は常に最新の count
の値を保持します。
callback 関数を渡す
setCount
関数に callback
関数を渡すことで、状態更新後に処理を実行することができます。
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count => {
console.log(count + 1); // 1 と出力される
return count + 1;
});
};
return (
<div>
<p>カウント:{count}</p>
<button onClick={handleClick}>カウントアップ</button>
</div>
);
このコードでは、setCount
関数に callback
関数を渡しています。callback
関数は、更新前の count
の値を受け取り、更新後の count
の値を返すことができます。
setState 関数を使う
useState
Hook で生成された setState
関数は、直接呼び出すことで再レンダリングをトリガーすることができます。
const [count, setState] = useState(0);
const handleClick = () => {
setState(count => count + 1);
};
return (
<div>
<p>カウント:{count}</p>
<button onClick={handleClick}>カウントアップ</button>
</div>
);
このコードでは、setState
関数を直接呼び出して count
の値を更新しています。
useState
で状態更新後に再レンダリングをトリガーするには、いくつかの方法があります。状況に応じて適切な方法を選択してください。
javascript reactjs react-hooks