React - Rules of Hooks - Conditional Hooks

Published on
4 mins read
––– views

1st React Hooks Rules:

We must call them within the scope of the React application. Calling an hook outside of a React component will throw an error.

React needs to manage the state and effects related to hooks through the component lifecycle

This rule does not apply to Custom Hooks.

2nd React Hooks Rule:

React hooks must be called at the top level of the component. We cannot call hooks conditionally or inside loops. React doesn't like it when someone else manages its state. React wants to manage its own state 100% or not at all - not in between.

Hooks rely on the order in which they are called, so when React manages the order and state, any deviation from the expected order can cause errors or bugs that are difficult to track down and fix in your app.

Class components are different, because we cannot use hooks with them. We need to have functional components to use Hooks Therefore, different rules apply when using class components because they rely on lifecycle methods to manage state and effects.

Why does the order matter for React Hooks?

When I first discovered the reason behind this rule, I was shocked, but apparently, that's how React works.

Here is an example snippet

function Gallery({ id, image }) {

 const [numberA, setNumberA] = React.useState(1)
 const [numberB, setNumberB] = React.useState(2)


  return (
   // code
  )}

When React renders this code, this is how it works:

1st - React.useState(1)
2nd - React.useState(2)

When React reads your useState hook, it doesn't assign [number, setNumberA] to useState(1). It simply grabs every hook it can find, and orders them. So, for React it self, this isn't happening const [numberA, setNumberA] = React.useState(1). React doesn't know, or care what does your first useState hook is assigned to.

Okay.. so what?

Let's grab the same code with a small twist:

function Gallery({ id, image }) {
 // condition
if(x){
 const [numberA, setNumberA] = React.useState(1)
}
 const [numberB, setNumberB] = React.useState(2)

 //

  return (
   // code
  )}

if X is true, the code will render as it should be:

1st - React.useState(1)
2nd - React.useState(2)

Here is the cath, if X is false:

// when true, 1st equals to 1
1st - React.useState(2)
2st - React.useState(2)

When we conditionally render React Hooks, React doesn't really care about the condition. It will grab all the hooks it can find and assign the values. React will grab the first hook but won't assign any value to it. It will then find the next hook and assign its value to the first hook.

It's important to remember that React is really good at managing its own state. If we try to manipulate its state with conditions, React will throw errors and generate bugs. So it's best to let React manage its state entirely or not at all.

What if we need to have a condition?

Well, there are many clever ways to handle this problem:

function Gallery({ id, image }) {
  const [imageSrc, setImageSrc] = React.useState('')

  const generatedId = React.useId()
  // this is our condition
  // id isn't valid, we will generate one.
  const appliedId = id || generatedId

  React.useEffect(() => {
    setImageSrc(fetchImageData(appliedId))
  }, [appliedId])

  return <div></div>
}

Here is an example of conditional useState hook

function MyComponent() {
  const [count, setCount] = React.useState(condition ? 0 : 10)

  //  code
}

here is an example of useState hook with 3 set conditions and a default

const [value, setValue] = React.useState(() => {
  if (condition1) {
    return 10
  } else if (condition2) {
    return 20
  } else if (condition3) {
    return 30
  }
  // default value
  return 0
})

We can also create switch statements

const [value, setValue] = React.useState(() => {
  switch (condition) {
    case 'A':
      return 10
    case 'B':
      return 20
    case 'C':
      return 30
    default:
      return 0
  }
})