Let’s talk about useEffects in React Hooks! I’m going to share with you 4 tips you should have in mind when using useEffect.
Use a useEffect for a single purpose
In React Hooks, you can have multiple useEffect functions. This is a great feature because, if we analyze how to write clean code, you’ll see that functions should serve a single purpose (much like how a sentence should communicate one idea only).
Splitting useEffects into short and sweet single-purpose functions also prevents unintended executions (when using the dependency array).
For example, let’s say you have varA that is unrelated to varB, and you want to build a recursive counter based on useEffect (with setTimeout) so let’s write some bad code:
|
|
As you can see, a single change in any of the variables varA and varB will trigger an update in both variables. This is why this hook doesn’t work properly.
Since this is a short example, you may feel that it’s obvious, however, in longer functions with more code and variables I guarantee you will miss this. So do the right thing and split your useEffect.
In this case, it should become:
|
|
Note: The code here is for example purposes only, which is meant to help you easily understand the problems of useEffect. Normally, when a variable depends on its’ previous state, the recommended way is to do setVarA(varA => varA + 1) instead.
Use custom hooks whenever you can
Let’s take the example above again. What if the variables varA and varB are completely independent?
In this case, we can simply create a custom hook to isolate each variable. This way, you get to know exactly what each function is doing to which variable.
Let’s build some custom hooks then!
|
|
Now each variable has its’ own hook. Much more maintainable and easy to read!
Conditionally run useEffect the right way
On the topic of setTimeout, let’s take the following example:
For some reason, you want to limit the counter to a maximum of 5. There’s the correct way and the incorrect way.
Let’s take a look at the incorrect way first:
Although this works, bear in mind that clearTimeout will run on any change of varA, while setTimeout is conditionally run.
The recommended way to run useEffect conditionally is to do a conditional return at the beginning of the function, like so:
This is what you see Material UI using (as well as many others) and it makes sure you’re not running useEffect by mistake.
Type out every prop inside useEffect in the dependency array
If you’re using ESLint, then you’ve probably seen a warning that comes from ESLint exhaustive-deps rule.
This is crucial. When your app grows bigger and bigger, more dependencies (props) get added into each useEffect. To keep track of all of them and avoid stale closures, you should add each and every dependency into the dependency array. (Here’s the official take on this subject)
Again, on the topic of setTimeout, let’s say you want to run setTimeout only once and add on to varA, much like the previous examples.
You might be tempted to do the following incorrect example:
Although this will do what you want, let’s take a moment to think, “what if your code gets bigger?”, or, “what if I want to change the code above to something else?”
In that case, you’ll want to have the variables all mapped out, as it will be much easier to test and detect problems that might arise (like stale props and closures).
The correct way should be:
|
|
That’s it, folks. If you have any questions or suggestions, I’m all ears! Reply or comment below and I’ll be sure to check it out!