Table of contents
Text Link
Text Link

Unveiling the Power of useEffect React Hook

In React, effects are actions in functional components that can influence components outside the pure rendering function and do not impact the rendering outcome. These can include working with APIs, changing the DOM, managing subscriptions, or setting timers.

useEffect runs effects after the component has rendered, allowing DOM updates, network requests, etc. without affecting the output of the component.

useEffect in React is used in cases like:

  • component rendering: Equivalent to componentDidMount in class components. Used to trigger effects (such as API requests) after the component has first rendered to the DOM.
  • component update: Equivalent to componentDidUpdate in class components. useEffect can monitor changes to certain values (states or props) and trigger effects when they change.
  • removing a component from the DOM: Equivalent to componentWillUnmount in class-based components. The useEffect React hook has the capability to provide a cleanup function that executes when the component is unmounted. This functionality proves helpful for tasks such as unsubscribing from events or clearing timers.

How to use Effects

useEffect takes two arguments:

  • Callback Function: This is the main function that will be executed after the component is rendered. This function can perform any side effects required by the component.
  • Dependencies Array: An optional array that contains the values that your effect depends on.

If the array is empty, the effect will only run once after the initial rendering -- check out the following code snippet:

useEffect(() => {

console.log(“useEffect”);

}, []);

If the dependency array contains data and one of these values changes, the effect will be re-triggered after the component updates:

const [count, setCount] = useState(0);

const [color, setColor] = useState("red”);

useEffect(() => {

console.log(count);

}, [count, color]);

If there is no array, then the function will be executed every time after each rendering:

useEffect(() => {

console.log(count);

}, );

Advanced use of the useEffect

Clean up the effect

In some cases, it's necessary to clean up the effects. For example, when an effect is no longer relevant after the props have changed, it needs to be “cleaned up.” To achieve this, you simply return a function from the useEffect hook, within which the cleanup activities are performed -- check out the following code snippet:

const [id, setId] = useState();

useEffect(() => {

let isSubscribed = true;

someAsyncOperation().then(result => {

if (isSubscribed) {

// your code

}

});

return () => {

isSubscribed = false;

};

}, [id]);

This trick works great for all kinds of functions, not just React effects!

Async-Await Operations in useEffect

To fetch data from an API, asynchronous operations are necessary. However, useEffect does not support async functions directly.

We have several options to perform asynchronous operations.

— Defining an async function FetchData inside useEffect:

const [id, setId] = useState();

useEffect(() => {

const fetchData = async () => {

const data = await fetchSomeData();

console.log(data);

};

fetchData();

}, [id]);

This is the most common and straightforward way to handle asynchronous operations inside useEffect.

— Using IIFE (Immediately Invoked Function Expression):

const [id, setId] = useState();

useEffect(() => {

(async () => {

const data = await fetchSomeData();

console.log(data);

})();

}, [id]);

This approach is similar to the first one but uses an IIFE for the immediate invocation of an async function.

— Create an asynchronous function outside the body of useEffect and then call it inside useEffect:

const [id, setId] = useState();

const fetchData = async () => {

const data = await fetchSomeData();

return data;

};

useEffect(() => {

fetchData();

}, [id]);

This async approach can be particularly useful when the same function is used in multiple effects or in other parts of your component. However, it's important to remember that if your function depends on props or state of the component, these dependencies must be properly managed to avoid outdated closures.

Custom Hooks

Create your own hooks using useEffect to reuse complex effect logic across different components.

export default function useUserData(userId) {

const [data, setData] = useState(null);

const [loading, setLoading] = useState(true);

const [error, setError] = useState(null);

useEffect(() => {

setData(null);

setLoading(true);

setError(null);

const fetchData = async () => {

try {

const response = await fetch(`https://api.example.com/users/${userId}`);

const userData = await response.json();

setData(userData);

} catch (error) {

setError(error);

} finally {

setLoading(false);

}

};

fetchData();

}, [userId]);

return { data, loading, error };

}

Then you can use it from any component like this:

Share this article
Get more articles
like this
Thank you! Your submission has been received!
Oops! Something went wrong.

Common Mistakes and Best Practices

Forgetting about dependencies:

A common mistake is not including all necessary dependencies in the effect's dependency array. This can lead to outdated data or infinite loops.

Best practice: Always include in the dependency array all values from the component's scope that are used in the effect and can change.

Using asynchronous functions directly in useEffect:

This can lead to warnings in the console because useEffect is not designed to work with promises directly.

Best practice: Wrap asynchronous operations in functions and call them inside the body of useEffect.

Creating unnecessary re-renders:

Due to incorrect definition of dependencies, your component may re-render more often than necessary.

Best practice: Carefully select dependencies and use useCallback and useMemo to prevent unnecessary effects.

Improper cleanup of effects:

Uncleared subscriptions or timers can cause memory leaks.

Best practice: Always return a cleanup function from the effect if it subscribes to any external data sources or utilizes resources that require cleanup.

Using useEffect for computations unrelated to side effects:

useEffect is intended for side effects, not for computations that should affect the rendering result.

Best practice: For computations that alter the output of a component, use useState and useMemo.

Conclusion

Using the React hook useEffect is a key element in creating efficient and functional React components. Once you master its usage, try not to overcomplicate the useEffect hook. Remember, it's better to use a separate hook for each effect for clarity and simplicity of code.

Create a free account to access the full topic

Wide range of learning tracks for beginners and experienced developers
Study at your own pace with your personal study plan
Focus on practice and real-world experience
Andrei Maftei
It has all the necessary theory, lots of practice, and projects of different levels. I haven't skipped any of the 3000+ coding exercises.