The infamous "Invalid Hook Call" error can be a massive headache, especially in complex applications using large JavaScript files. The reason this error is obnoxious is that it is found in runtime, so you will not know it exists until you run your application.
In this article, we dive into common causes for this error, some examples, and fixes.
Understanding the Error
Error Code in Development:
Invalid hook call. Hooks can only be called inside of the body of a function component.
Error Code in Production (Minified):
Minified React error #321; visit https://reactjs.org/docs/error-decoder.html?invariant=321
This is likely one of the errors you saw in your console that led you to this article.
These errors often stem from breaking one of React’s Hook Rules, or mismanaging your React setup.
Causes of "Invalid Hook Call"
Breaking the Rules of Hooks (most common):
- See bottom of article for the 10 react hook rules
Mismatching React Versions:
- Ensure the same version of React and React DOM is used.
Duplicate React Copies:
- Happens when dependencies include their own React instance.
Calling Hooks Outside React Contexts:
- Example: Using a hook in a regular function.
Debugging the Error
Check Your Hook Placement: Ensure hooks are only used in:
Validate React Versions: Run npm list react react-dom to confirm versions match.
Inspect for Duplicate React Copies: Run npm ls react to ensure only one React version is installed.
Examine the Stack Trace: The browser console often points to the file and line number (roughly) causing the issue.
My Personal Example
The Problem
Here’s an example of my code that was violating the Rules of Hooks by calling useLocation() in a regular function, which is not a React component:
export function links() {
const location = useLocation();
return [{ rel: 'canonical', href: `https://example.com${location.pathname}` }];
}
Why This Fails:
- Hooks like
useLocation() can only be called in a React function component or a custom hook, not in regular functions.
The Solution
Move the useLocation() call to a valid React component and pass its value down:
export function links(pathname) {
return [{ rel: 'canonical', href: `https://example.com${pathname}` }];
}
export default function App() {
const location = useLocation();
return (
<head>
<Links pathname={location.pathname} />
</head>
);
}
Why This Works:
- The
useLocation hook is now called within a valid React component (App), and its result is passed to the links function.
The 10 Rules of React Hooks (With Examples and Fixes)
If you are getting this error, usually it is because you are breaking one of the below rules.
1. Call Hooks at the Top Level
Hooks should not be placed in conditionals or loops.
if (someCondition) {
useEffect(() => { }, []);
}
useEffect(() => {
if (someCondition) {
}
}, []);
2. Call Hooks Only in Function Components or Custom Hooks
Hooks cannot be used in regular JavaScript functions.
3. Use the Correct Dependencies
Include all variables used inside useEffect or other hooks as dependencies.
useEffect(() => {
console.log(count);
}, []);
useEffect(() => {
console.log(count);
}, [count]);
4. Create Reusable Logic With Custom Hooks
Extract shared logic into custom hooks.
function useFetchData(url) {
const [data, setData] = useState();
useEffect(() => {
fetch(url).then(response => response.json()).then(setData);
}, [url]);
return data;
}
This is more so advice than a rule, but it will massively clean up your code if you understand how to abstract reused logic into your React hooks.
5. Maintain the Same Order of Hooks
The order of hooks must be consistent. Here are some ways hooks can remain inconsistent:
Using hooks in conditionals, loops or functions
Dynamically loading components that contain hooks (based on screen size, variables etc)
Returning from a function before a potential hook call
if (isMobile) {
useEffect(() => {
console.log("This runs only if isMobile is true.");
}, [isMobile]);
}
items.forEach((item) => {
useState(item);
});
function handleNested() {
useState('nested');
}
if (items.length > 3) {
handleNested();
}
if (isMobile) {
useEffect(() => {
console.log("Conditional rendering logic with hooks");
}, []);
}
if (!items || items.length === 0) {
return <div>No items</div>;
}
useEffect(() => {
if (isMobile) {
console.log("Do something when isMobile is true.");
}
}, [isMobile]);
6. Use the use Prefix for Custom Hooks
This helps distinguish hooks from regular functions.
function useCustomHook() {
return useState();
}
7. Do Not Call Hooks in Non-React Code
Avoid using hooks in unrelated files or external utilities.
8. Avoid Hooks Inside Async Functions
Hooks cannot be called within asynchronous code directly.
useEffect(async () => {
const data = await fetchData();
}, []);
useEffect(() => {
fetchData().then(data => setData(data));
}, []);
9. Use Hooks in React Contexts
Hooks like useContext require a valid context provider.
const MyContext = React.createContext();
function MyComponent() {
const value = useContext(MyContext);
}
10. Avoid Direct State Mutation
React state must be updated using the setState function.
state.value = 42;
setState({ value: 42 });
Takeaways
When debugging an "Invalid Hook Call" error:
Check your hook usage against React’s Hook Rules.
Validate your project setup for version mismatches or duplicate React copies.
Review stack traces and error messages for insights.
By following this guide, you can avoid this error, and ensure smooth development in your React projects.