This error occurs when React's useCallback Hook receives a function that cannot be properly memoized, often due to incorrect dependencies or function definition issues. It typically indicates a misuse of the useCallback API that prevents React from caching the function across re-renders.
The "useCallback: the provided function is invalid" error is React's way of signaling that the function passed to useCallback cannot be memoized correctly. This usually happens when the function reference changes on every render despite having the same dependencies, or when the dependencies array is malformed. useCallback is designed to return the same function instance when dependencies haven't changed, but certain patterns can break this guarantee. The hook needs to be able to compare the current function with the previous one, and if the function definition itself is unstable or depends on values that React cannot track properly, it cannot maintain the memoization guarantee. This error often appears in development mode when React detects that a useCallback hook is not functioning as intended, either because the function is being redefined in a way that creates a new reference each time, or because the dependencies are incorrectly specified.
Ensure the useCallback call happens at the top level of your component, not inside loops, conditions, or nested functions. This is a fundamental rule of Hooks:
// Correct: Top level
function MyComponent() {
const handleClick = useCallback(() => {
// ...
}, []);
return <button onClick={handleClick}>Click</button>;
}
// Incorrect: Inside condition
function MyComponent({ showButton }) {
if (showButton) {
const handleClick = useCallback(() => { // Violates Rules of Hooks
// ...
}, []);
}
}Review all values used inside the callback function. Every reactive value (props, state, context) must be included in the dependency array. Use the ESLint plugin for React to automatically detect missing dependencies:
// Before: Missing userId dependency
const handleClick = useCallback(() => {
console.log(userId); // userId from props or state
}, []); // Empty array - will cause invalid function warning
// After: Include all dependencies
const handleClick = useCallback(() => {
console.log(userId);
}, [userId]); // Now correctly includes userIdIf a dependency changes on every render (like an object literal {} or array []), memoize it with useMemo or move it outside the component:
// Before: Object literal changes every render
const handleSubmit = useCallback(() => {
api.post('/endpoint', { userId }); // { userId } is new object each time
}, [userId]); // Still recreates on every render
// After: Memoize the config object
const config = useMemo(() => ({ userId }), [userId]);
const handleSubmit = useCallback(() => {
api.post('/endpoint', config);
}, [config]); // Only recreates when userId changesFor callbacks that depend on previous state, useReducer often provides a more stable function reference because the dispatch function is constant:
// Before: Multiple state dependencies
const [count, setCount] = useState(0);
const [text, setText] = useState('');
const handleAction = useCallback(() => {
setCount(count + 1);
setText(text + '!');
}, [count, text]); // Recreates when count or text changes
// After: useReducer provides stable dispatch
const [state, dispatch] = useReducer(reducer, initialState);
const handleAction = useCallback(() => {
dispatch({ type: 'INCREMENT_AND_APPEND' });
}, []); // dispatch is stable, no dependencies neededInstead of defining the function separately and passing it to useCallback, define it directly as the first argument:
// Before: Function defined separately
function myFunction() {
console.log('clicked');
}
const handleClick = useCallback(myFunction, []); // Can cause issues
// After: Define directly in useCallback
const handleClick = useCallback(() => {
console.log('clicked');
}, []); // Clearer and more reliableIf you need to reference a value inside the callback but don't want changes to that value to recreate the callback, store it in a ref:
const userIdRef = useRef(userId);
userIdRef.current = userId; // Keep ref updated
const handleClick = useCallback(() => {
console.log(userIdRef.current); // Access via ref
}, []); // Empty array - callback never recreatesWarning: Use this pattern carefully as it can lead to stale closures if not managed properly.
Understanding React's memoization: useCallback works by comparing the current dependency array with the previous one. If all dependencies are equal by reference (using Object.is), React returns the previously memoized function. If any dependency has changed, it creates and returns a new function.
The GitHub issue #14099: The React team has acknowledged that useCallback can invalidate too often in practice. This happens because developers often include values in dependency arrays that change frequently (like inline objects/arrays) or because they misunderstand what should be a dependency.
When not to use useCallback: Not every function needs to be memoized. useCallback adds overhead and complexity. Only use it when:
1. The function is passed as a prop to a component wrapped in React.memo
2. The function is a dependency of another hook (useEffect, useMemo, etc.)
3. You have measured a performance issue and confirmed useCallback helps
React Compiler future: The experimental React Compiler (formerly React Forget) aims to automatically memoize components and hooks without requiring manual useCallback. When stable, it may reduce the need for manual memoization.
Debugging tips: Use React DevTools Profiler to see when components re-render. Add console.log inside your useCallback to see how often it recreates. Check if dependencies are truly stable with useMemo.
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
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite