This error occurs when you accidentally pass an object (using curly braces {}) instead of an array (using square brackets []) as the second argument to useCallback. React requires the dependency list to be an array, not a plain object.
The useCallback hook in React requires exactly two arguments: the callback function to memoize, and an array of dependencies that determines when the callback should be recreated. This error appears when you mistakenly pass a plain object `{}` instead of an array `[]` as the second parameter. While both arrays and objects are technically "objects" in JavaScript, React specifically requires the dependency list to be an array. The hook uses this array to track changes in dependencies using the Object.is comparison algorithm. When you pass an object instead of an array, React cannot properly track dependency changes, and the memoization behavior breaks. This is typically a simple syntax mistake - using curly braces when you meant to use square brackets. The fix is straightforward: replace the object notation with proper array syntax for the dependency list.
Find the useCallback hook that's causing the error. Look for code that passes curly braces as the second argument:
// WRONG - using object syntax
const memoizedCallback = useCallback(() => {
doSomething(value);
}, { value }); // ❌ Object instead of array
// WRONG - empty object
const memoizedCallback = useCallback(() => {
doSomething();
}, {}); // ❌ Empty object instead of empty arrayThe error message in your console will typically point to the specific file and line number where this occurs.
Change the second argument from object notation {} to array notation []:
// CORRECT - using array syntax
const memoizedCallback = useCallback(() => {
doSomething(value);
}, [value]); // ✅ Array with dependency
// CORRECT - empty dependency array
const memoizedCallback = useCallback(() => {
doSomething();
}, []); // ✅ Empty arrayThis is the core fix - ensuring the second parameter is an array, not an object.
Include all values from component scope that the callback uses:
function MyComponent({ userId, onUpdate }) {
const [count, setCount] = useState(0);
// Include all external values the callback references
const handleClick = useCallback(() => {
console.log('User:', userId, 'Count:', count);
onUpdate(userId, count);
}, [userId, count, onUpdate]); // ✅ All dependencies listed
return <button onClick={handleClick}>Click me</button>;
}React will recreate the callback whenever any value in this dependency array changes.
If you need to depend on object values, extract specific properties or use useMemo:
// OPTION 1: Depend on specific object properties
const handleUpdate = useCallback(() => {
updateUser(user.id, user.name);
}, [user.id, user.name]); // ✅ Specific properties, not entire object
// OPTION 2: Memoize the object first
const stableConfig = useMemo(() => ({
timeout: 1000,
retries: 3
}), []); // Memoize object
const processData = useCallback(() => {
fetchData(stableConfig);
}, [stableConfig]); // ✅ Stable object referencePassing entire objects as dependencies can cause unnecessary re-renders because objects are compared by reference, not by value.
Install and configure the React Hooks ESLint plugin to catch dependency issues automatically:
npm install eslint-plugin-react-hooks --save-devAdd to your ESLint configuration:
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}The exhaustive-deps rule will warn you about missing dependencies and incorrect dependency array syntax.
Verify that your callback is now memoizing correctly:
function TestComponent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Callback executed');
}, []); // Empty array - never recreated
// This ref will track callback identity
const callbackRef = useRef(handleClick);
useEffect(() => {
if (callbackRef.current !== handleClick) {
console.log('Callback changed'); // Should not log if dependencies unchanged
}
callbackRef.current = handleClick;
}, [handleClick]);
return (
<div>
<button onClick={() => setCount(count + 1)}>Re-render</button>
<ChildComponent onAction={handleClick} />
</div>
);
}The child component should not re-render when the parent re-renders if the callback is properly memoized.
When to use empty dependency array: Using useCallback(() => {}, []) with an empty array means the callback is created once and never recreated. This is useful for event handlers that don't depend on component state or props, but be careful - if your callback references stale values, you'll have bugs.
Object.is comparison: React uses Object.is() to compare dependencies between renders. This means 0 and -0 are different, but NaN equals NaN (unlike the === operator). Objects and arrays are compared by reference, not by contents.
Performance considerations: Don't overuse useCallback. Only use it when: (1) passing callbacks to optimized child components wrapped in React.memo, (2) the callback is used as a dependency in other hooks like useEffect, or (3) the callback creation is computationally expensive. In most cases, recreating a function is faster than the memoization overhead.
Common misunderstanding: Some developers mistakenly try to pass configuration objects to useCallback thinking it accepts options similar to other APIs. The second parameter is always a dependency array, never a configuration object.
TypeScript protection: If you're using TypeScript with proper React types, the type system will catch this error at compile time: Type 'object' is not assignable to type 'DependencyList | undefined'. This makes the error much easier to catch before runtime.
React 19+ improvements: Future versions of React may introduce useEvent (previously called useEffectEvent) which provides a way to create stable callback references without dependency arrays, solving many common useCallback pain points.
React.FC expects children prop to be defined
React.FC no longer includes implicit children prop
Warning: You provided a `selected` prop to a form field without an `onChange` handler
You provided a 'selected' prop without an onChange handler
Failed to load source map from suspense chunk
How to fix "Failed to load source map from suspense chunk" in React
Prop spreading could cause security issues
Prop spreading could cause security issues
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency