Lesson 4. What are hooks anyway? 4. Rules for hooks
You noticed that each call to a hook was referred to by an index when rendering a component several times. The first hook, then the second, etc. It might seem weird, but React has its reasons for this behavior. And what is more important is the consequence it has.
Since each hook call is referred to by its index, this index must remain
consistent from a rendering to the next one. If the first hook is a useState
storing the name at the first rendering, it cannot be another state storing the
user ID at the second one, nor can it be a useEffect.
It implies that you cannot use hooks in conditions, loops, or any function body.
if (id === 0) {// Using a hook inside a condition is forbidden!useEffect(() => alert('Wrong ID'), [id])}const getUserName = (id) => {// Using a hook inside a function is forbidden!useEffect(() => {fetch(...)}, [id])}
It is also not possible to return something prematurly before a hook call:
const Division = ({ numerator, denominator }) => {if (denominator === 0) return <p>Invalid denominator</p>// Using a hook after a `return` is forbidden.const [result, setResult] = useState(undefined)useEffect(() => {setResult(numerator / denominator)}, [numerator, denominator])return <p>Result = {result}</p>}
We can simplify the rules on hooks this way: we must do all calls to hooks at
the root of the component function body and before any return.
You may think of it as a contraint, but in most cases it is not that hard to
find another way. For instance, instead of having a useEffect inside a if,
you can put the if inside the useEffect:
useEffect(() => {if (id === 0) {alert('Wrong ID')}}, [id])
To avoid calling hooks after a return, you may have to use some tricks.
const Division = ({ numerator, denominator }) => {const [result, setResult] = useState(undefined)const [invalid, setInvalid] = useState(false)useEffect(() => {if (denominator === 0) {setInvalid(true)setResult(undefined)} else {setInvalid(false)setResult(numerator / denominator)}}, [numerator, denominator])if (invalid) {return <p>Invalid denominator</p>} else {return <p>Result = {result}</p>}}