This error occurs when you pass an object as the second argument to React's useEffect hook instead of an array of dependencies. The useEffect hook expects its dependencies to be in an array format for proper comparison between renders. This mistake often leads to infinite re-renders or the effect not running when expected.
The useEffect hook in React is designed to run side effects after render. Its second parameter is a dependency array that tells React when to re-run the effect based on changes to the listed values. When you accidentally pass an object instead of an array, React can't properly track dependencies, leading to unexpected behavior. React compares dependency values using Object.is() for equality checking. When you pass an object as the second argument, React sees it as a single dependency that changes reference on every render (since objects are compared by reference, not by value). This causes the effect to run on every render, potentially creating infinite loops or performance issues. The error message specifically mentions receiving an "object" type, but this warning can also appear for other non-array types like numbers, booleans, or strings passed as the second argument.
Check your console warning to see which component contains the error. Look for useEffect calls where the second argument might be an object.
// Wrong - passing an object directly
useEffect(() => {
fetchData();
}, { userId, limit }); // Object instead of array
// Wrong - object literal without array
useEffect(() => {
updateTitle();
}, config); // config is an object
// Wrong - using curly braces instead of square brackets
useEffect(() => {
console.log('Effect runs');
}, { userId }); // Curly braces create objectReplace the object with an array containing the dependencies. If you have multiple dependencies, list them inside the array.
// Correct - single dependency in array
useEffect(() => {
fetchData();
}, [userId]);
// Correct - multiple dependencies in array
useEffect(() => {
updateTitle();
}, [userId, limit]);
// Correct - empty array for no dependencies
useEffect(() => {
initialize();
}, []);
// Correct - object properties as separate dependencies
useEffect(() => {
fetchData();
}, [config.userId, config.limit]); // Extract propertiesIf you need to use an object as a dependency (for example, configuration objects), memoize it with useMemo to maintain referential equality.
function UserComponent({ userId, settings }) {
// Memoize the config object
const config = useMemo(() => ({
userId,
limit: settings.limit,
sortBy: settings.sortBy
}), [userId, settings.limit, settings.sortBy]);
// Now config can be used as a dependency
useEffect(() => {
fetchUserData(config);
}, [config]); // config reference is stable when dependencies don't change
return <div>User data</div>;
}Whenever possible, use individual primitive values in the dependency array rather than entire objects. This is more predictable and avoids reference comparison issues.
// Instead of passing the entire config object:
useEffect(() => {
fetchData(config);
}, [config]); // Object reference changes on every render
// Pass the specific primitive values needed:
useEffect(() => {
fetchData({ userId, limit });
}, [userId, limit]); // Primitives compared by value
// Or extract properties from the object:
const { userId, limit } = config;
useEffect(() => {
fetchData({ userId, limit });
}, [userId, limit]);Use eslint-plugin-react-hooks to automatically catch dependency array issues. This plugin will warn you about incorrect dependency specifications.
Install the plugin if not already present:
npm install eslint-plugin-react-hooks --save-devAdd to your .eslintrc configuration:
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}The exhaustive-deps rule will highlight dependency array problems in your editor, including when you pass non-array values.
Run your application in development mode and verify the warning no longer appears. Test that effects run at the correct times.
npm run dev
# or
npm startNavigate to the affected component and check:
- No console warnings about useEffect arguments
- Effect runs when dependencies actually change
- Effect doesn't run unnecessarily
- Application behavior is correct
Reference Equality vs. Value Equality: React uses Object.is() for dependency comparison, which means objects and arrays are compared by reference, not by content. Two objects with identical properties are considered different if they have different references. This is why passing new object literals causes effects to re-run.
Empty Array vs. No Array: An empty dependency array [] means the effect runs once after initial mount. Omitting the array entirely means the effect runs after every render. Passing an object (or any non-array) is invalid and triggers this warning.
React 18 Strict Mode: In React 18's Strict Mode during development, effects run twice to help identify side effects. This is normal and helps catch bugs early.
Custom Comparison Hooks: For complex cases where you need deep object comparison, consider libraries like react-use's useDeepCompareEffect or implementing custom comparison logic with useRef and useEffect.
TypeScript Support: TypeScript can help catch this error at compile time by enforcing the correct type for the second argument (DependencyList | undefined).
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