Back to Re-render Optimization

Use Functional setState Updates

reacthooksuseStateuseCallbackcallbacksclosures
28.0k🕒 2026-06-10Source ↗

Install this skill

npx skills add vercel-labs/agent-skills

Works across Claude Code, Cursor, Codex, Copilot & Antigravity

Functional setState updates provide a pattern in React where the state setter receives a callback rather than a raw value. This method accepts the most current state as an argument, allowing you to compute new values without capturing them in a closure. By decoupling the update logic from the component's rendered state snapshot, you eliminate the need to include state variables in hook dependency arrays. This results in stable function identities, which minimizes unnecessary re-renders of memoized child components. Beyond performance, this approach is the primary defense against stale closure bugs, ensuring that rapid or asynchronous state updates always process the most accurate, up-to-date data. It shifts the responsibility of tracking state versioning to the React runtime, creating cleaner and more predictable component lifecycle management.

When to Use This Skill

  • Incrementing counters based on previous values
  • Removing items from an array inside a memoized handler
  • Managing complex object state updates inside hooks
  • Performing sequential updates within async blocks

How to Invoke This Skill

Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:

  • refactor this code to remove stale closures
  • fix unnecessary re-renders in my component callbacks
  • how to update state without including it in dependency array
  • optimize my useCallback hooks for performance
  • ensure my state updates use the latest values

What this skill does

  • Eliminates stale closure bugs by accessing current state snapshots
  • Removes unnecessary state dependencies from useCallback hooks
  • Stabilizes callback references to prevent downstream re-renders
  • Enables safe concurrent state updates in asynchronous operations
  • Simplifies dependency management inside event handlers

When not to use it

  • When setting state to a static, known value
  • When updating state strictly based on new props or user input
  • When the next state value is entirely independent of the current state

Example workflow

  1. Identify a component where state updates trigger frequent re-renders of children
  2. Pinpoint useCallback hooks that list state variables in their dependency arrays
  3. Replace direct state setters with the functional callback syntax
  4. Remove the now-unnecessary state dependencies from the dependency array
  5. Verify that the callback identity remains stable across re-renders

Prerequisites

  • Basic knowledge of React useState hook
  • Understanding of JavaScript closures
  • Familiarity with useCallback dependencies

Pitfalls & limitations

  • !Overusing functional updates when a simple static value setter suffices
  • !Assuming functional updates are necessary for all setter calls, which adds unnecessary boilerplate
  • !Misunderstanding that while functional updates fix stale closures, they do not change the execution order of React updates

FAQ

Does this replace the need for React Compiler?
No, while the React Compiler automates some memoization, functional updates remain a best practice for correctness and avoiding stale closure bugs.
Is functional state updating slower?
No, the overhead is negligible and the performance gains from reduced re-renders usually outweigh the cost of the function wrapper.
Can I use async/await inside the functional update?
Functional updates are synchronous by design to ensure consistent state transitions; keep async logic outside the setter.

How it compares

Generic prompts often suggest using refs to bypass state issues, whereas functional updates solve the same problem while keeping state strictly within the React data flow.

Source & trust

28k stars🕒 Updated 2026-06-10
📄 Full skill instructions — original source: vercel-labs/agent-skills
## Use Functional setState Updates

When updating state based on the current state value, use the functional update form of setState instead of directly referencing the state variable. This prevents stale closures, eliminates unnecessary dependencies, and creates stable callback references.

**Incorrect (requires state as dependency):**

function TodoList() {
const [items, setItems] = useState(initialItems)

// Callback must depend on items, recreated on every items change
const addItems = useCallback((newItems: Item[]) => {
setItems([...items, ...newItems])
}, [items]) // ❌ items dependency causes recreations

// Risk of stale closure if dependency is forgotten
const removeItem = useCallback((id: string) => {
setItems(items.filter(item => item.id !== id))
}, []) // ❌ Missing items dependency - will use stale items!

return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} />
}


The first callback is recreated every time items changes, which can cause child components to re-render unnecessarily. The second callback has a stale closure bug—it will always reference the initial items value.

**Correct (stable callbacks, no stale closures):**

function TodoList() {
const [items, setItems] = useState(initialItems)

// Stable callback, never recreated
const addItems = useCallback((newItems: Item[]) => {
setItems(curr => [...curr, ...newItems])
}, []) // ✅ No dependencies needed

// Always uses latest state, no stale closure risk
const removeItem = useCallback((id: string) => {
setItems(curr => curr.filter(item => item.id !== id))
}, []) // ✅ Safe and stable

return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} />
}


**Benefits:**

1. **Stable callback references** - Callbacks don't need to be recreated when state changes
2. **No stale closures** - Always operates on the latest state value
3. **Fewer dependencies** - Simplifies dependency arrays and reduces memory leaks
4. **Prevents bugs** - Eliminates the most common source of React closure bugs

**When to use functional updates:**

- Any setState that depends on the current state value
- Inside useCallback/useMemo when state is needed
- Event handlers that reference state
- Async operations that update state

**When direct updates are fine:**

- Setting state to a static value: setCount(0)
- Setting state from props/arguments only: setName(newName)
- State doesn't depend on previous value

**Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler can automatically optimize some cases, but functional updates are still recommended for correctness and to prevent stale closure bugs.

How to Use This Skill Unit

Option A: Project-Specific (Recommended)

  1. Click "Download" above
  2. In your project, create the directory: .agent/skills/rerender-functional-setstate/
  3. Save the file as SKILL.md
  4. The agent will automatically discover the skill based on its description.

Option B: Global Installation (All Agents)

Save the file to these locations to make it available across all projects:

  • Claude Code: ~/.claude/skills/vercel-labs/agent-skills/rerender-functional-setstate/SKILL.md
  • Cursor: ~/.cursor/skills/vercel-labs/agent-skills/rerender-functional-setstate/SKILL.md
  • Antigravity: ~/.gemini/antigravity/skills/vercel-labs/agent-skills/rerender-functional-setstate/SKILL.md

🚀 Install with CLI:
npx skills add vercel-labs/agent-skills

Read the Master Guide: Mastering Agent Skills

Recommended Rules

View more rules

Recommended Workflows

View more workflows

Recommended MCP Servers

View more MCP servers

Take It Further

Maximize your productivity with these powerful resources

📋

Define Your Standards

Set up coding standards to ensure this workflow produces consistent, high-quality results.

Browse Rules Library
📖

Master Workflows

Learn how to create custom workflows, use Turbo Mode, and build your automation library.

Complete Guide

How to use this Skill in Claude Code & Cursor

For Claude Code (CLI)

To use this skill in Claude Code, copy the rule content into your project's custom instructions or follow our Add-Skill CLI guide. This ensures Claude follows your standards during every code generation.

For Cursor & Windsurf

For Cursor or Windsurf, individual skills are best used in the "Rules for AI" section. This specific unit helps the agent avoid re-render optimization issues, leading to cleaner, more efficient code.

Why the skill format matters: the standardized Agent Skills format lets your AI agent load detailed instructions only when they are relevant, keeping your prompt clean while improving results.

Source & attribution

This skill is categorized under Re-render Optimization and is published by Vercel Engineering, maintained in vercel-labs/agent-skills.

← Browse All Agent Skills
Sponsored AI assistant. Recommendations may be paid.