@raegen/react-use-prev
v0.1.0
Published
The correct usePrevious: renders the previous distinct value immediately, purely, with no extra render.
Maintainers
Readme
@raegen/react-use-prev
The usePrevious hook that's actually correct when you render the previous value.
import { usePrev } from '@raegen/react-use-prev';
const prev = usePrev(value);Returns the previous distinct value of value: the value as of the most recent commit
at which Object.is saw it change. Correct, rendered immediately, pure, and with no extra
render.
Why another one?
Every popular usePrevious is wrong in a different way when you display the previous value,
not just diff it inside an effect:
| Property | react-use usePrevious | ref-in-effect | usehooks-ts¹ | usePrev |
|---|:---:|:---:|:---:|:---:|
| Correct previous-distinct value | ❌ collapses | ✅ | ✅ | ✅ |
| Rendered immediately (no lag) | ❌ wrong value | ❌ one behind | ✅ | ✅ |
| No ref mutation during render | ✅ | ✅ | ❌ | ✅ |
| Self-delivering (no extra trigger needed) | ❌ | ❌ | n/a | ✅ |
| No extra render | ✅ | ✅ | ✅ | ✅ |
¹ usehooks-ts usePrevious and react-use usePreviousDistinct share the same mechanism
(refs mutated during render), so they share the same failure mode: impure under StrictMode
and concurrent rendering.
- react-use
usePreviousreturns "value at last render," so any unrelated re-render collapses the previous value into the current one. - The ref-in-effect pattern renders one transition behind: the ref is advanced after the render that needed it, and a ref write schedules no re-render.
- usehooks-ts tracks the value correctly but mutates refs during render, which breaks the rules of React under StrictMode double-invocation and concurrent rendering.
usePrev reads a local ref during render (never mutates one there) and advances it only in a
post-commit effect. The read-time fallback returns the soon-to-be-previous value on the
transition render, so the correct value is on screen immediately with no extra render.
Install
npm install @raegen/react-use-prevreact is a peer dependency (>=16.8). Works with React 16.8 through 19.
Usage
import { usePrev } from '@raegen/react-use-prev';
function Price({ amount }: { amount: number }) {
const prevAmount = usePrev(amount);
const direction = prevAmount === undefined ? null : amount > prevAmount ? '▲' : '▼';
return <span>{amount} {direction}</span>;
}Provide a second argument to set the first-render value (otherwise it's undefined):
const prev = usePrev(value, value); // first render returns the current value instead of undefinedThe invariant
On every committed render,
usePrevreturns the value held at the previous commit whereObject.is(value, previousValue)wasfalse— orinitialValue(defaultundefined) before any such change.
Types
function usePrev<T>(value: T): T | undefined; // no initial value
function usePrev<T>(value: T, initialValue: T): T; // with an initial valueThe first render returns initialValue, so without one the return type is T | undefined;
with one it's T.
Gotchas
Comparison is Object.is — the same comparison React uses for dependency arrays. If you
pass a referentially-unstable value (a fresh object/array literal every render), every render
counts as a change, exactly as it would in a useEffect dependency array. Pass primitives or
memoized values. A custom comparator (for deep/structural equality) is planned for a future
release.
It's a client hook. Like any hook using useEffect, call it from a Client Component.
Its first render is deterministic (initialValue), so it hydrates without a mismatch.
License
MIT
