This error occurs when useMemo receives a second argument that is not an array, or when the dependency array format is invalid. The dependencies parameter must be passed as an array literal to properly track reactive values.
This error indicates that the second argument passed to React's useMemo hook is not in the expected array format. The useMemo hook expects two arguments: a calculation function and a dependency array. The dependency array tells React when to recalculate the memoized value by tracking reactive values like props, state, and local variables. When you pass something other than an array as the second argument (such as an object, function, or omit it entirely with incorrect syntax), React cannot properly track dependencies and throws this error. The error message specifically mentions receiving an "object" type instead of the expected array, which typically happens when curly braces are used instead of square brackets, or when the dependency argument is malformed. React's static analysis and ESLint plugins rely on the dependency array being a literal array to verify that all reactive values are correctly specified as dependencies. Without a proper array format, these tools cannot perform their checks, leading to potential bugs where memoization doesn't work as expected.
Check that your useMemo call follows the correct format with square brackets for the dependency array:
// ✅ Correct - dependency array with square brackets
const memoizedValue = useMemo(() => expensiveCalculation(a, b), [a, b]);
// ❌ Wrong - object literal with curly braces
const memoizedValue = useMemo(() => expensiveCalculation(a, b), {a, b});
// ❌ Wrong - parentheses instead of brackets
const memoizedValue = useMemo(() => expensiveCalculation(a, b), (a, b));
// ❌ Wrong - missing array wrapper
const memoizedValue = useMemo(() => expensiveCalculation(a, b), a, b);Ensure you're using square brackets [] to wrap all dependencies.
Verify that the syntax between the calculation function and dependency array is correct:
// ✅ Correct - comma between function and array
const value = useMemo(() => {
return expensive(data);
}, [data]);
// ❌ Wrong - missing comma
const value = useMemo(() => {
return expensive(data);
} [data]);
// ❌ Wrong - extra comma creating unexpected behavior
const value = useMemo(() => expensive(data),, [data]);The comma separating the two arguments is essential for proper parsing.
React's linter and static analysis require the dependency array to be defined inline as a literal:
// ✅ Correct - inline array literal
const result = useMemo(() => compute(x, y), [x, y]);
// ⚠️ Discouraged - variable reference (can't be statically verified)
const deps = [x, y];
const result = useMemo(() => compute(x, y), deps);
// ❌ Wrong - object passed as second argument
const config = { dependencies: [x, y] };
const result = useMemo(() => compute(x, y), config);Always define the array directly in the useMemo call for proper linting support.
List every variable, prop, or state value used inside the calculation function:
function MyComponent({ data, filter }) {
const [sortOrder, setSortOrder] = useState('asc');
// ✅ Correct - all reactive values included
const sortedData = useMemo(() => {
return data
.filter(item => item.type === filter)
.sort((a, b) => sortOrder === 'asc' ? a.value - b.value : b.value - a.value);
}, [data, filter, sortOrder]);
// ❌ Wrong - missing dependencies
const sortedData = useMemo(() => {
return data.filter(item => item.type === filter).sort(...);
}, [data]); // Missing: filter, sortOrder
}Enable the eslint-plugin-react-hooks to catch missing dependencies automatically.
When dependencies are objects or arrays, ensure they have stable references:
function MyComponent({ config }) {
// ❌ Wrong - new object created on every render
const result = useMemo(() => {
return process(config);
}, [{ setting: config.setting }]); // New object reference each time
// ✅ Better - depend on primitive values
const result = useMemo(() => {
return process(config);
}, [config.setting, config.option]);
// ✅ Best - if you must pass whole object, memoize it first
const memoizedConfig = useMemo(() => ({
setting: config.setting,
option: config.option
}), [config.setting, config.option]);
const result = useMemo(() => {
return process(memoizedConfig);
}, [memoizedConfig]);
}Avoid creating new object/array literals inside the dependency array.
If the calculation should only run once and has no dependencies, use an empty array:
// ✅ Correct - empty array for one-time calculation
const initialData = useMemo(() => {
return generateExpensiveData();
}, []);
// ❌ Wrong - omitting the array entirely (runs every render)
const initialData = useMemo(() => {
return generateExpensiveData();
});
// ⚠️ Note: For truly constant data, consider useState with initializer instead
const [initialData] = useState(() => generateExpensiveData());An empty dependency array means the calculation runs only on mount.
If using TypeScript, ensure your dependency array typing is correct:
import { useMemo } from 'react';
interface Props {
items: string[];
multiplier: number;
}
function MyComponent({ items, multiplier }: Props) {
// ✅ Correct - TypeScript can infer the dependency array type
const processed = useMemo(() => {
return items.map(item => item.length * multiplier);
}, [items, multiplier]);
// ❌ Wrong - trying to type the dependency parameter
const processed = useMemo(() => {
return items.map(item => item.length * multiplier);
}, [items, multiplier] as [string[], number]); // Unnecessary and can cause issues
}TypeScript automatically validates dependency array types when using proper syntax.
Common Pattern Confusion: Developers sometimes confuse useMemo with useEffect syntax. While both accept dependency arrays, useEffect doesn't return a value and useMemo is synchronous. The useMemo hook must always return a value from its calculation function.
ESLint Configuration: Install and configure eslint-plugin-react-hooks to catch dependency issues automatically. Add to your ESLint config:
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/exhaustive-deps": "warn"
}
}Performance Consideration: useMemo adds overhead. Only use it for expensive calculations or when passing object/array references to child components that might trigger unnecessary re-renders. Don't memoize everything by default.
Dependency Array Size Changes: Another related warning is "The final argument passed to useMemo changed size between renders." This occurs when the dependency array length changes dynamically, which React doesn't allow. The array must have a constant size and order. If you need variable dependencies, restructure your component logic.
React Compiler: Future versions of React may include a compiler that automatically handles memoization, potentially making manual useMemo usage less common. However, understanding proper dependency management remains crucial for React development.
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