ReactでuseStateの前回の値を表示する方法

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)」のように使う分だけスライスしてください。

ReactでuseStateの前回の値を表示するサンプル