React displays a warning when useEffect receives a second argument that is not an array. The second parameter must be an array of dependencies or omitted entirely.
This warning occurs when you pass a non-array value as the second parameter to the useEffect Hook. React's useEffect accepts an optional dependency array as its second argument to control when the effect runs. If you provide this argument, it must be an array containing the dependencies your effect relies on. React compares the values in this array between renders to determine if the effect should re-run. Passing any other type (like a number, boolean, string, object, or undefined) causes this warning because React cannot perform the dependency comparison it needs. The warning message specifically tells you what type you accidentally passed instead of an array, making it easier to identify where the mistake occurred in your code.
Check your console warning to see which component and line contains the error. The warning will tell you what type was received instead of an array.
Look for useEffect calls in your component:
// Wrong - passing a number
useEffect(() => {
console.log('Effect runs');
}, 0);
// Wrong - passing a boolean
useEffect(() => {
fetchData();
}, true);
// Wrong - passing single value without array
useEffect(() => {
updateTitle();
}, count);Wrap your dependency in square brackets to create an array. If you have no dependencies, use an empty array. If you want the effect to run on every render, omit the second argument entirely.
// Correct - empty array (runs once on mount)
useEffect(() => {
console.log('Effect runs once');
}, []);
// Correct - single dependency in array
useEffect(() => {
updateTitle();
}, [count]);
// Correct - multiple dependencies in array
useEffect(() => {
fetchData();
}, [userId, searchTerm]);
// Correct - no second argument (runs on every render)
useEffect(() => {
console.log('Runs on every render');
});Ensure any variable, prop, or state used inside your effect is included in the dependency array. This is critical for correct behavior.
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [theme, setTheme] = useState('light');
// Include ALL variables used inside the effect
useEffect(() => {
fetchUser(userId).then(data => {
setUser(data);
applyTheme(theme);
});
}, [userId, theme]); // Both userId and theme must be listed
return <div>{user?.name}</div>;
}Use the eslint-plugin-react-hooks to automatically catch missing or incorrect dependencies. This plugin will warn you about dependency array issues.
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 any dependency array problems in your editor.
Objects and arrays are compared by reference in JavaScript, which can cause effects to re-run unnecessarily. Use useMemo or useCallback to stabilize these values.
function SearchComponent({ config }) {
const [results, setResults] = useState([]);
// Wrong - config object changes reference every render
useEffect(() => {
search(config).then(setResults);
}, [config]); // This will run on every render
// Better - memoize the config object
const memoizedConfig = useMemo(() => config, [
config.query,
config.limit,
config.filters
]);
useEffect(() => {
search(memoizedConfig).then(setResults);
}, [memoizedConfig]);
// Or extract primitive values
const { query, limit } = config;
useEffect(() => {
search({ query, limit }).then(setResults);
}, [query, limit]); // Use primitives directly
}Run your application in development mode and check that the warning no longer appears in the console. Test the component behavior to ensure effects run at the correct times.
npm run dev
# or
npm startNavigate to the affected component and verify:
- No console warnings appear
- Effect runs when dependencies change
- Effect doesn't run unnecessarily
- Application behavior is correct
Search for other instances of useEffect to ensure consistent correct usage across your project.
# Search for useEffect usage
grep -r "useEffect" src/
# Use your IDE's search to find all occurrencesLook for patterns like:
- useEffect with no second argument when dependencies should be specified
- useEffect with empty array when dependencies are actually needed
- Any remaining non-array second arguments
Understanding Dependency Comparison: React uses Object.is() for comparing dependency values between renders. This means primitive values (numbers, strings, booleans) are compared by value, while objects and arrays are compared by reference. Two objects with identical contents but different references are considered different.
Empty Array vs. No Array: An empty dependency array [] means the effect runs once after the initial render (component mount). Omitting the array entirely means the effect runs after every render. Choose based on your specific needs.
React 18 Strict Mode: In React 18's Strict Mode during development, effects run twice to help identify side effects and bugs. This is intentional and only happens in development mode, not production.
Custom Hooks and Dependencies: When creating custom hooks that use useEffect internally, ensure you document what dependencies the hook expects and properly pass them through from the consuming component.
Performance Considerations: While it might be tempting to pass an empty array to avoid re-running effects, this can lead to stale closures where your effect uses outdated values. Always include actual dependencies to ensure correct behavior, and use memoization techniques if performance becomes a concern.
TypeScript Integration: If using TypeScript, the dependency array is typed as DependencyList | undefined, which is defined as ReadonlyArray<any> | undefined. This type constraint helps catch non-array arguments at compile time.
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