
useStateの前回の値を表示
ボタンを押した際にuseStateにランダムの値を渡して表示するには以下のようなコードになります。
App.tsx
import { useState } from 'react'
function App() {
const [num, setNum] = useState<number | null>(null)
return (
<>
<p>現在のnum: {num}</p>
<button onClick={() => setNum(Math.floor(Math.random() * 99))}>
update
</button>
</>
)
}
export default App
ReactではuseStateがよく使われるので、これはほとんどの人ができると思います。
では、以下のように現在の値だけでなく、前回の値を表示するにはどうすればよいのでしょうか?
例えば{num}が15のときにボタンを押して{num}が33になったら、{prevNum}は15になるイメージです。
<p>現在のnum: {num}</p>
<p>前回のnum: {prevNum}</p>
useRefで前回のuseStateの値を保存する
前回のuseStateの値を保存するためにuseRefが使用されることがあります。
まず、useRefを使ってprevNumRefという変数を作成します。
const prevNumRef = useRef<number | null>(null)
次にuseEffect内でnumが更新された際に、prevNumRefにnumの値を代入します。
useEffect(() => {
prevNumRef.current = num
}, [num])
あとは {prevNumRef.current} を記述すれば、前回のuseStateの値を表示できます。
完成形のコードは以下の通り。
App.tsx
import { useRef, useEffect, useState } from 'react'
function App() {
const [num, setNum] = useState<number | null>(null)
const prevNumRef = useRef<number | null>(null)
useEffect(() => {
prevNumRef.current = num
}, [num])
return (
<>
<p>現在のnum: {num}</p>
<p>前回のnum: {prevNumRef.current}</p>
<button onClick={() => setNum(Math.floor(Math.random() * 99))}>
update
</button>
</>
)
}
export default App
しかし、この方法だと前々回のnumを表示するなど、機能を拡張する際に変数が増えてしまいます。
あとuseRefとuseEffectが追加されており、使用するReactフックも追加されているため、見通しの悪いコードになってしまいます。
useStateの配列に過去の値を保存する
useStateの過去に使用した値を保存するには、useStateで配列を作成します。
TSX
const [numHistory, setNumHistory] = useState<number[]>([])
過去のuseStateの値は配列に保存した値を {numHistory[0]} や {numHistory[1]} で指定することで表示できます。
App.tsx
import { useState } from 'react'
function App() {
const [num, setNum] = useState<number | null>(null)
const [numHistory, setNumHistory] = useState<number[]>([])
const updateNum = () => {
if (num !== null) {
setNumHistory([num, ...numHistory.slice(0, 1)])
}
setNum(Math.floor(Math.random() * 99))
}
return (
<>
<p>現在のnum: {num}</p>
<p>前回のnum: {numHistory[0]}</p>
<p>前々回のnum: {numHistory[1]}</p>
<button onClick={updateNum}>
update
</button>
</>
)
}
export default App
[num, …numHistory] だと配列が実行するたびに大きくなってしまうので、必ず「…numHistory.slice(0, 1)」のように使う分だけスライスしてください。