usePrevious
The usePrevious
hook allows you to keep track of the previous value of a state or prop in a React component. It stores all previous values in a Map
, which can be useful for debugging or tracking changes over time.
Add the utility
npx @ivnatsr/ezreact add use-previous
import { useState, useEffect, useMemo } from 'react'
export function usePrevious({ value, entriesKeysFormatter = () => Date.now(), debug = false}) { const [currentValue, setCurrentValue] = useState(value) const [previousValue, setPreviousValue] = useState(undefined) const [allPreviousValues, setAllPreviousValues] = useState(() => new Map())
if (currentValue !== value) { setPreviousValue(currentValue) setAllPreviousValues((prev) => { const newMap = new Map(prev) const entryKey = entriesKeysFormatter(currentValue) newMap.set(entryKey, currentValue) return newMap }) setCurrentValue(value) }
useEffect(() => { if (!debug) return if (allPreviousValues.size === 0) { console.info('[usePrevious DEBUG]: No previous values') return }
console.log('[usePrevious DEBUG]:')
for (const [key, value] of allPreviousValues) { console.info(`Entry key: ${key}, Value: ${value}`) }
console.log('_________________') }, [allPreviousValues, debug])
return { previousValue, allPreviousValues: useMemo( () => Array.from(allPreviousValues.entries()), [allPreviousValues] ) }}
import { useState, useEffect, useMemo } from 'react'
export function usePrevious<ValueType, PreviousValueEntryType = number>({ value, entriesKeysFormatter = () => Date.now() as PreviousValueEntryType, debug = false}: { value: ValueType entriesKeysFormatter?: (value: ValueType) => PreviousValueEntryType debug?: boolean}) { const [currentValue, setCurrentValue] = useState(value) const [previousValue, setPreviousValue] = useState<ValueType | undefined>(undefined) const [allPreviousValues, setAllPreviousValues] = useState< Map<PreviousValueEntryType, ValueType> >(() => new Map())
if (currentValue !== value) { setPreviousValue(currentValue) setAllPreviousValues((prev) => { const newMap = new Map(prev) const entryKey = entriesKeysFormatter(currentValue) newMap.set(entryKey, currentValue) return newMap }) setCurrentValue(value) }
useEffect(() => { if (!debug) return if (allPreviousValues.size === 0) { console.info('[usePrevious DEBUG]: No previous values') return }
console.log('[usePrevious DEBUG]:')
for (const [key, value] of allPreviousValues) { console.info(`Entry key: ${key}, Value: ${value}`) }
console.log('_________________') }, [allPreviousValues, debug])
return { previousValue, allPreviousValues: useMemo( () => Array.from(allPreviousValues.entries()), [allPreviousValues] ) }}
Parameters
value
: The current value to track.entriesKeysFormatter
(optional): A function that formats the key for each entry in the previous values map. Defaults to a function that returns the current timestamp.debug
(optional): A boolean that enables debug logging. Defaults tofalse
.
Return
This hook returns an object containing:
previousValue
: The previous value of the tracked state or prop.allPreviousValues
: An array of entries from the map containing all previous values, formatted as[key, value]
.
Example
import { useState } from 'react'import { usePrevious } from './path/to/use-previous'
const PreviousValueExample = () => { const [count, setCount] = useState(0) const { previousValue, allPreviousValues } = usePrevious({ value: count })
return ( <div> <h1>Current Count: {count}</h1> <h2>Previous Count: {previousValue}</h2> <button onClick={() => setCount(count + 1)}>Increment</button> <h3>All Previous Values:</h3> <ul> {allPreviousValues.map(([key, value]) => ( <li key={key}>Key: {key}, Value: {value}</li> ))} </ul> </div> )}
export default PreviousValueExample