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
}
})