How to fix useState received extra argument in React

ReactBEGINNERMEDIUMUpdated Dec 8, 2025ยท 104 views

This error occurs when attempting to pass a callback function as a second argument to the useState setter, similar to class component setState. Unlike class components, useState setters only accept the new state value and do not support callback arguments.

Quick fix

Fixes useState received extra argument

// INCORRECT - This causes the error
setState(newValue, () => {
  console.log('State updated');
});
  1. Identify all useState setters receiving extra arguments
  2. Replace callback pattern with useEffect
  3. Handle conditional effect execution if needed
  4. Use functional setState for previous value access
react โ€” error
// INCORRECT - This causes the error
setState(newValue, () => {
console.log('State updated');
});
useState received extra argument

What this error means

This error appears when developers try to use the useState setter function with a second callback argument, mimicking the behavior of class component setState. In class components, setState accepts an optional callback function as a second parameter that executes after the state update completes. However, the useState hook in functional components was designed differently - the setter function returned by useState only accepts a single argument: the new state value or an updater function. The React team made a conscious design decision not to include callback support in useState. This was intentional, not an oversight. The rationale was that the useEffect and useLayoutEffect hooks provide a cleaner, more declarative way to handle side effects after state changes. This design encourages better separation of concerns between state updates and side effects. When you pass a second argument to a useState setter, React throws this error because the function signature doesn't support it. The error typically surfaces during development when migrating code from class components to functional components, or when developers unfamiliar with hooks attempt to replicate class component patterns.

Typical symptoms

  • Application crashes immediately when setState is called with two arguments
  • Console error appears: "setState received extra argument" or similar warning
  • Callback function passed to useState setter never executes
  • State updates correctly but subsequent code that depends on the new state value fails
  • TypeScript shows type error when attempting to pass second argument to setState
  • Code that worked in class components breaks after refactoring to functional components
  • useEffect with state dependency runs before callback logic would have executed

Common causes

  1. Passing a callback function as the second argument to the useState setter, copying the class component setState pattern
  2. Migrating from class components to functional components without updating setState callback patterns
  3. Attempting to execute logic immediately after state update using a callback instead of useEffect
  4. Misunderstanding the useState API signature and assuming it matches class component setState
  5. Following outdated tutorials or examples that confuse class and functional component patterns
  6. Using custom hooks or third-party libraries that incorrectly wrap useState and accept extra arguments
  7. Accidentally passing additional parameters when calling the setter function in event handlers
  8. Trying to access the updated state value synchronously after calling setState with a callback

How to fix "useState received extra argument"

1Identify all useState setters receiving extra arguments

Search your codebase for patterns where useState setters are called with two arguments. Look for code like this:

javascript
// INCORRECT - This causes the error
setState(newValue, () => {
  console.log('State updated');
});

Use your IDE's search functionality or grep to find instances of , after setState calls. Check all event handlers, async functions, and effect hooks where state is being updated.

2Replace callback pattern with useEffect

The recommended approach is to use useEffect with a dependency array containing the state variable. This effect will run whenever the state changes:

javascript
import { useState, useEffect } from 'react';

function MyComponent() {
  const [age, setAge] = useState(0);

  // This runs after age updates
  useEffect(() => {
    console.log('Age updated to:', age);
    // Your callback logic here
  }, [age]);

  const handleClick = () => {
    setAge(21); // No second argument
  };

  return <button onClick={handleClick}>Update Age</button>;
}

The dependency array [age] tells React to run the effect only when age changes. This provides the same functionality as the class component callback.

3Handle conditional effect execution if needed

If your callback should only run under specific conditions (not on initial render), use a ref to track whether it's the first render:

javascript
import { useState, useEffect, useRef } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);
  const isFirstRender = useRef(true);

  useEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false;
      return; // Skip effect on first render
    }

    // This only runs after subsequent updates
    console.log('Count changed:', count);
  }, [count]);

  return <button onClick={() => setCount(count + 1)}>Increment</button>;
}

This pattern prevents the effect from running on component mount, only triggering after state updates.

4Use functional setState for previous value access

If you need to access the previous state value while updating, use the functional form of setState instead of a callback:

javascript
// INCORRECT - Trying to access previous value with callback
setState(newValue, (prevValue) => {
  console.log('Previous:', prevValue);
});

// CORRECT - Use functional update
setState(prevValue => {
  console.log('Previous:', prevValue);
  return prevValue + 1;
});

The functional form receives the current state as an argument and returns the new state. This is especially important when state updates depend on the previous value.

5Consider custom hooks for complex callback logic

If you need useState-with-callback functionality throughout your app, create a custom hook:

javascript
import { useState, useEffect, useRef } from 'react';

function useStateCallback(initialState) {
  const [state, setState] = useState(initialState);
  const callbackRef = useRef(null);

  const setStateWithCallback = (newState, callback) => {
    callbackRef.current = callback;
    setState(newState);
  };

  useEffect(() => {
    if (callbackRef.current) {
      callbackRef.current(state);
      callbackRef.current = null;
    }
  }, [state]);

  return [state, setStateWithCallback];
}

// Usage
const [age, setAge] = useStateCallback(0);
setAge(21, (updatedAge) => {
  console.log('Age is now:', updatedAge);
});

This provides a class-component-like API while maintaining hooks compatibility. Use this sparingly - direct useEffect is generally clearer.

6Update TypeScript types if using custom solutions

If you're using TypeScript and implementing custom callback solutions, ensure proper typing:

typescript
import { useState, useEffect, useRef, Dispatch, SetStateAction } from 'react';

type Callback<T> = (value: T) => void;
type SetStateWithCallback<T> = (
  newState: SetStateAction<T>,
  callback?: Callback<T>
) => void;

function useStateCallback<T>(
  initialState: T
): [T, SetStateWithCallback<T>] {
  const [state, setState] = useState<T>(initialState);
  const callbackRef = useRef<Callback<T> | null>(null);

  const setStateWithCallback: SetStateWithCallback<T> = (newState, callback) => {
    callbackRef.current = callback || null;
    setState(newState);
  };

  useEffect(() => {
    if (callbackRef.current) {
      callbackRef.current(state);
      callbackRef.current = null;
    }
  }, [state]);

  return [state, setStateWithCallback];
}

This provides full type safety for your custom hook implementation.

7Test state update timing and asynchronous behavior

Verify that your refactored code handles the asynchronous nature of state updates correctly:

javascript
function MyComponent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('Effect runs with count:', count);
  }, [count]);

  const handleClick = () => {
    console.log('Before update:', count);
    setCount(count + 1);
    console.log('After setState (still old value):', count);
    // The effect will run after this function completes
  };

  return <button onClick={handleClick}>Count: {count}</button>;
}

Remember that setState is asynchronous - you cannot access the new value immediately after calling it. The useEffect runs after the component re-renders with the new state.

Advanced notes

useLayoutEffect vs useEffect for callbacks: If your callback needs to run synchronously before the browser paints (similar to componentDidUpdate in class components), use useLayoutEffect instead of useEffect. This is important for DOM measurements or preventing visual flicker, but comes with a performance cost since it blocks the browser paint.

Multiple state updates in one render: If you need to update multiple state values and run a callback only after all have updated, combine them into a single state object or use useReducer. Each individual setState call can trigger a separate re-render, and useEffect will run for each change unless you batch them.

Batching behavior differences: React 18 introduced automatic batching for all state updates, including those in promises, setTimeout, and native event handlers. In React 17 and earlier, only updates in React event handlers were batched. This affects when your useEffect callbacks run - multiple setState calls might trigger the effect only once in React 18.

Custom hook libraries: Several community packages provide useState-with-callback functionality, including "use-state-with-callback" and "use-state-callback". Evaluate whether the added dependency is worth the convenience, as the useEffect pattern is generally preferred and more idiomatic.

Debugging infinite loops: When migrating callback logic to useEffect, ensure your dependency array is correct. Missing or incorrect dependencies can cause infinite render loops. If the effect updates state that's in its own dependency array, you'll create an infinite loop. Use React DevTools Profiler to detect this.

Class component migration checklist: When converting from class components, map this.setState's second argument to useEffect with appropriate dependencies, ensure componentDidMount logic goes in useEffect with empty array [], and componentDidUpdate conditional logic becomes useEffect with specific dependencies. The patterns are different but equivalent in functionality.

Frequently asked questions

What does "useState received extra argument" mean?
This error appears when developers try to use the useState setter function with a second callback argument, mimicking the behavior of class component setState. In class components, setState accepts an optional callback function as a second parameter that executes after the state update completes. However, the useState hook in functional components was designed differently - the setter function returned by useState only accepts a single argument: the new state value or an updater function.
How do I fix "useState received extra argument"?
This error occurs when attempting to pass a callback function as a second argument to the useState setter, similar to class component setState. Unlike class components, useState setters only accept the new state value and do not support callback arguments.
What causes "useState received extra argument"?
Passing a callback function as the second argument to the useState setter, copying the class component setState pattern Migrating from class components to functional components without updating setState callback patterns Attempting to execute logic immediately after state update using a callback instead of useEffect
Is the "useState received extra argument" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading