useEffect: Best Practices for Using and Avoiding it.

Aakash Garg
4 min readNov 29, 2023

--

Photo by Lautaro Andreani on Unsplash.

By now, we all must be aware of the usage of useEffect hook in React and must know how & when it is to be used. It is a powerful tool enables us to perform tasks such as fetching data, setting up subscriptions, and updating the DOM in response to component updates.

However, while the useEffect hook is a great solution for many use cases, it may not always be the best choice due to potential performance concerns. In certain situations, using the useEffect hook can lead to unnecessary re-renders and slow down the performance of our application.

We’ll discuss some of them one by one :

1. You don’t need useEffect for transforming data

Consider an example like this:

function Cart() {
const [items, setItems] = useState([]);
const [total, setTotal] = useState(0);

useEffect(() => {
setTotal(
items.reduce((currentTotal, item) => {
return currentTotal + item.price;
}, 0)
);
}, [items]);
}

On every re-render the intention is to calculate the total as and when items[] changes. As the total is a derived value we can wrap it inside a useMemo hook as such:

function Cart() {
const [items, setItems] = useState([]);
const total = useMemo(() =>
items.reduce((currentTotal, item) => {
return currentTotal + item.price;
}, 0),[items]);
}

Honestly the useMemo here is still an overkill and only if you encounter performance problems should you use it. We can narrow it down to simply:

2. You don’t need useEffect for communicating with parents

Consider an example :

function Product({ onOpen, onClose }) {
const [isOpen, setIsOpen] = useState(false);

useEffect(() => {
if (isOpen) {
onOpen();
} else {
onClose();
}
}, [isOpen]);

return (
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
);
}

We’re toggling isOpen state and calling the appropriate parent callbacks over here. We can get rid of useEffect here and instead use a temporary variable to maintain the opposite boolean state as such:

3. You don’t need useEffect for subscribing to external stores

useEffect is really good for subscriptions right?. Well consider an example :

function Store() {
const [isConnected, setIsConnected] = useState(false);

useEffect(() => {
const sub = storeAPI.subscribe(({ status }) => {
setIsConnected(status === 'connected');
});

return () => {
sub.unsubscribe();
};
}, []);
}

Over here, I’m subscribing to some sort of storeAPI and all I’m doing is extracting whether I’m connected to the storeAPI and unsubscribing it.

Well we already have a hook for it called useSyncExternalStore which takes 3 parameters subscribe (function that subscribes to the store), getSnapshot (function returning snapshot of the data) & getServerSnapshot(optional function returning snapshot of data in the store), we can make use if this hook as such:

4.You don’t need useEffect for initializing global singletons.

Consider a simple example like this:

function doSomething() {
useEffect(() => {
storeApi.authenticate();
}, []);
}

We have done this a lot where we execute some sort of effect right when our app starts & intutively useEffect is the right place for that. Not that related but in React18 this runs twice!. Of course you can fix that by having a boolean variable that tracks if the effect has ran the first time and playing with it(still on overkill). Lemme show you how simple it can be:

5. You don’t need useEffect to handle user events

Consider you are building a form and on mount it sends a analytics event and upon filling the form and clicking Submit it sends POST request to the /register endpoint.

function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [jsonToSubmit, setJsonToSubmit] = useState(null);


useEffect(() => {
post('/analytics/event', { eventName: 'visit_form' });
}, []);

useEffect(() => {
if (jsonToSubmit !== null) {
post('/api/register', jsonToSubmit);
}
}, [jsonToSubmit]);

function handleSubmit(e) {
e.preventDefault();
setJsonToSubmit({ firstName, lastName });
}
// ...
}

As per React’s official docs:

When you’re not sure whether some code should be in an Effect or in an event handler, ask yourself why this code needs to run. Use Effects only for code that should run because the component was displayed to the user.

For out example above the api/register POST request isn’t caused by the form being displayed. It is only sent when the user presses the Submit button. We can delete the second useEffect and fix the above case scenario as such:

Conclusion

Congratulations!. You’ve now learned the relevant usage of useEffect and how to apply them to create scalable and maintainable code.

Best of luck in your development journey! 😄🚀

--

--