React Hook is Called Conditionally

Error: React Hook is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a react hook after an early return? occurs when hooks are invoked conditionally or after a return of a value. When we deal with react hooks we have to take care of the following things.

  1. Don’t call Hooks inside loops, conditions, or nested functions.
  2. Don't use a return statement before invoking the hooks.

Let us discuss this in detail.

Don’t call Hooks inside loops, conditions, or nested functions

The hooks executed conditionally can lead to unexpected output and it is very hard to debug errors. So, the react hooks should be invoked in the same order between renderings.

Don’t call Hooks inside loops, conditions, or nested functions.

Don’t call hooks from regular JavaScript functions. Instead, call react hooks from custom hooks

Let's take an example of the below code which throws React error: react hook is called conditionally.

import { useState, useEffect } from 'react';

function App(){
  	const [count, setCount] = useState(0);
  	
  	
	// Throws Error: React Hook "useEffect" is called conditionally
  	if(count > 10){
    	useEffect(()=>{
      		setCount(10);
    	});
  	}
  	return (<div> {count} </div>);
}

export default App;

In this example, we are invoking the useEffect inside an if statement if(count > 10). This cause the error.

Solution:

To solve this issue, we can move the condition inside the useEffect as given below.

useEffect(()=>{
	if(count > 10){
        setCount(10);
  	}
});

Final code will look like:

import { useState, useEffect } from 'react';

function App(){
  	const [count, setCount] = useState(0); 
  	useEffect(()=>{
  		if(count > 10){
        	setCount(10);
      	}
    });  
  	return (<div> {count} </div>);
}

export default App;

 

Don't use a return statement before invoking the hooks

Always use hooks at the top level of your react function, before any early returns.

Here is an example of how the error occurs.

import React, { useEffect, useState } from "react"

function App(){
  	const [isLoading, setIsLoading] = useState(true)

	// Throws Error: React Hook "useEffect" is called conditionally
  	if (isLoading) {
    	return <label>Loading..</label>
  	}

  	useEffect(() => {
    	// Do something
    	setIsLoading(false)
  	}, [])
	
  	return (<div>App</div>);
}

export default App

React is throwing the above error because we are calling useEffect after the return statement( return <label>Loading..</label>). Make sure all hooks are defined before any return statement.

Solution

To solve the error, we can reorder the return statement and call react hooks at the top level as given below.

import React, { useEffect, useState } from "react"

const App = () => {
  	const [isLoading, setIsLoading] = useState(true)

  	useEffect(() => {
    	// Do something
    	setIsLoading(false)
  	}, []);

  	if (isLoading) {
    	return <label>Loading..</label>
  	}

  	return (<div>App</div>);
}

export default App

 

Use ESLint Plugin to enforce react hook rules

Use the ESLint plugin called eslint-plugin-react-hooks that enforces rules for hooks. we can install this plugin to our project using the below command.

npm install eslint-plugin-react-hooks --save-dev

ESLint configuration is given below.

// Your ESLint configuration
{
  "plugins": [
    // ...
    "react-hooks"
  ],
  "rules": {
    // ...
    "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
    "react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
  }
}

For more deep knowledge, read react doc.