React introduces hooks in 16.8 and everything changed. Hooks provided an opportunity to make functional components with state and lifecycles. The new approach required redefined mental models of how to write code in React. Not fully switching to a new approach lead to issues.
The work of a technical leader is full of decisions. You need to make them at the start and they can have an impact on the project even on the finish line. This series of article is for people who want to learn from my mistakes:
- React hooks dependencies
Situation
Let’s imagine we have a root component with a state that changes frequently and a component connected to the backend.
This code update Root
component state every 3s. Every time counter is incremented ArticlesWithMutipleRerenders
component is re-rendered. This leads to the calling getArticles
method every time the counter is changing.
Why is this happen?
React uses reference equality when comparing dependencies to figure out it should run useEffect
callback or not. In this case the following assign:
const db = DBConnection();
every render db
changes its reference, even if the value of this variable is the same as before.
That’s why useEffect
callback runs every time component is rendered:
useEffect(() => {
console.count("get articles");
db.getArticles().then(setArticles);
}, [db]);
Bad decision
My decision seemed the easiest one and the simplest. When I was using db
with useEffect
or any hook, I just omitted this dependency. Code looked like this and everything was fine at the beginning:
const db = DBConnection();
useEffect(() => {
console.count("get articles");
db.getArticles().then(setArticles);
}, []);
The worst decision
One thing still bothered me. I got a warning from eslint that db
dependency should be included in the dependency array.
After the bad decision, there was an even worse one. I suppressed all of that in-place with eslint-disable-next-line
. This led me to make it in every file multiple times and code loose much readability.
The right way
Everything we need to do is memoize db
and add it to the dependency array.
Why is this the best way?
-
DBConnection
can make multiple instances of the database connection. We want to keep as few connections as possible. That’s why we are creating one instance of the database connection. - Passing
db
instance ensure that when instance changes we fetch articles again.
const db = useMemo(() => DBConnection(), []);
useEffect(() => {
console.count("get articles");
db.getArticles().then(setArticles);
}, [db]);
Conclusion
The right code you can check on codesandbox. Making mistakes is the way we learn. Development is making decisions and draws conclusions based on results.
Let me know in the comments below if you have any questions. You can find me on Twitter.
Top comments (0)