usePrevious
Track the previous value of state or props. Essential for comparing current vs previous values in animations, transitions, and change detection.
Live Demo
See how usePrevious tracks the previous value across different types of state.
🔢 Counter Example
Current: 0
Previous: undefined
✏️ Text Input Example
Current: (empty)
Previous: (empty)
🎨 Color Picker Example
Current Color
#667eea
Previous Color
undefined
💡 How It Works:
- Returns the value from the previous render
- First render returns undefined (no previous value)
- Perfect for comparing current vs previous state/props
- Useful for animations, transitions, and change detection
Code Examples
Basic Usage
import { usePrevious } from "@uiblock/hooks";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
const previousCount = usePrevious(count);
return (
<div>
<p>Current: {count}</p>
<p>Previous: {previousCount ?? 'undefined'}</p>
<p>Change: {previousCount !== undefined ? count - previousCount : 'N/A'}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}Detecting Direction of Change
import { usePrevious } from "@uiblock/hooks";
import { useState } from "react";
export default function ScrollDirection() {
const [scrollY, setScrollY] = useState(0);
const previousScrollY = usePrevious(scrollY);
useEffect(() => {
const handleScroll = () => setScrollY(window.scrollY);
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const direction = previousScrollY !== undefined
? scrollY > previousScrollY ? '⬇️ Down' : '⬆️ Up'
: '—';
return <div>Scroll Direction: {direction}</div>;
}Animating Value Changes
import { usePrevious } from "@uiblock/hooks";
import { useState, useEffect } from "react";
export default function AnimatedNumber() {
const [value, setValue] = useState(0);
const previousValue = usePrevious(value);
const [isAnimating, setIsAnimating] = useState(false);
useEffect(() => {
if (previousValue !== undefined && previousValue !== value) {
setIsAnimating(true);
const timer = setTimeout(() => setIsAnimating(false), 300);
return () => clearTimeout(timer);
}
}, [value, previousValue]);
return (
<div
style={{
transform: isAnimating ? 'scale(1.2)' : 'scale(1)',
transition: 'transform 0.3s',
color: value > (previousValue ?? 0) ? 'green' : 'red'
}}
>
{value}
</div>
);
}Comparing Props Changes
import { usePrevious } from "@uiblock/hooks";
import { useEffect } from "react";
interface Props {
userId: string;
}
export default function UserProfile({ userId }: Props) {
const previousUserId = usePrevious(userId);
useEffect(() => {
if (previousUserId && previousUserId !== userId) {
console.log(`User changed from ${previousUserId} to ${userId}`);
// Fetch new user data, show transition, etc.
}
}, [userId, previousUserId]);
return <div>User ID: {userId}</div>;
}How It Works
Here's the implementation:
import { useRef, useEffect } from 'react'
export function usePrevious<T>(value: T): T | undefined {
const ref = useRef<T>()
useEffect(() => {
ref.current = value
}, [value])
return ref.current
}Key Features:
- Uses useRef to store value without causing re-renders
- Updates ref in useEffect after render completes
- Returns undefined on first render (no previous value)
- Works with any type: primitives, objects, arrays
API Reference
Parameters
value: TThe current value to track
Returns
T | undefined - The value from the previous render, or undefined on first render
💡 Use Cases
- Detecting direction of change (up/down, increase/decrease)
- Animating transitions between values
- Comparing current vs previous props in components
- Tracking scroll direction
- Form field change detection
- Undo/redo functionality
- Analytics and change tracking
- Conditional effects based on value changes