What this article series are about 🔍
This article series are basically a summary of You Might Not Need an Effect, which is a React official documentation about the usage of useEffect hook in React.
The original article is really really good, for sure better than my summary. So, if you have time, I highly recommend you to read the original article.
My motivation is just to provide a quick summary both for myself and for others who might be busy or a bit lazy to read through the whole article.
Yeah, it’s for you 😉 haha
I summarized the content as short as possible, straight to the point and code snippet.
So don’t forget to check the original article for more detailed information. Again, it’s really good learning material.
Specifically, I’ll skip Notifying parent components about state changes and Sending a POST request
because it’s basically about “do it inside of event handler function”, redundant with other examples.
Also skipping Passing a data to the parent, because it’s not mainly about the use of useEffect, but rather about the flow of data in React.
We shouldn’t use useEffect on event. Just do it with event handler function
This one seems to be a bit obvious, but it’s worth to mention.
I actually couldn’t come up with any case we have to use useEffect for event-specific logic.
Please let me know if you come up with any.
Bad 👎
const addToCart = (product) => {
product.isInCart = true;
};
function ProductPage({ product, addToCart }) {
// 🔴 Avoid: Event-specific logic inside an Effect
useEffect(() => {
if (product.isInCart) {
showNotification(`Added ${product.name} to the shopping cart!`);
}
}, [product]);
function handleBuyClick() {
addToCart(product);
}
function handleCheckoutClick() {
addToCart(product);
navigateTo(‘/checkout‘);
}
// …
}
Good 👍
If you want to create some shared logic between event handlers, just create a function.
// ✅ Good: Event-specific logic is called from event handlers
function buyProduct() {
addToCart(product);
showNotification(`Added ${product.name} to the shopping cart!`);
}
function handleBuyClick() {
buyProduct();
}
function handleCheckoutClick() {
buyProduct();
navigateTo(‘/checkout‘);
}
}
Takeaway
(Almost) Never use useEffect for event-specific logic. Just call the logic directly from the event handler function.
Reference
Sharing logic between event handlers
Consider if you can do it just during rendering, or inside of event handler
This example was introduced as Chains of computations in the original documentation.
I think it can be a good practice to ** consider how we can remove as much useEffect as possible from the Bad 👎 example.**
Why we need to reduce useEffect?
Re-render happens after each setState function in each of useEffect
Complexity. It’s hard to understand the flow of the code if we have too many useEffect
Bad 👎
const [card, setCard] = useState(null);
const [goldCardCount, setGoldCardCount] = useState(0);
const [round, setRound] = useState(1);
const [isGameOver, setIsGameOver] = useState(false);
useEffect(() => {
if (card !== null && card.gold) {
setGoldCardCount((c) => c + 1);
}
}, [card]);
useEffect(() => {
if (goldCardCount > 3) {
setRound((r) => r + 1);
setGoldCardCount(0);
}
}, [goldCardCount]);
useEffect(() => {
if (round > 5) {
setIsGameOver(true);
}
}, [round]);
useEffect(() => {
alert(‘Good game!‘);
}, [isGameOver]);
function handlePlaceCard(nextCard) {
if (isGameOver) {
throw Error(‘Game already ended.‘);
} else {
setCard(nextCard);
}
}
}
Good 👍 (After we remove all unnecessary useEffect)
const [card, setCard] = useState(null);
const [goldCardCount, setGoldCardCount] = useState(0);
const [round, setRound] = useState(1);
// const [isGameOver, setIsGameOver] = useState(false); // We don’t need this state
// Instead, ✅ calculate what you can during rendering
const isGameOver = round > 5;
function handlePlaceCard(nextCard) {
if (isGameOver) {
throw Error(‘Game already ended.‘);
}
// ✅ Calculate all the next state in the event handler, rather than in useEffect
setCard(nextCard);
if (nextCard.gold) {
if (goldCardCount <= 3) {
setGoldCardCount(goldCardCount + 1);
} else {
setGoldCardCount(0);
setRound(round + 1);
if (round === 5) {
alert(‘Good game!‘);
}
}
}
}
}
Takeaway
Try to consider if we can do it just during rendering, or inside of event handler.
Reference
See you again in #3!