We Might Not Need an Effect #2

RMAG news

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 👎

// Let’s assume this function is defined elsewhere
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.

function ProductPage({ product, addToCart }) {
// ✅ 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 👎

function Game() {
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)

function Game() {
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

Chains of computations

See you again in #3!

Leave a Reply

Your email address will not be published. Required fields are marked *