Olena Drugalya's Blog

Olena Drugalya's Blog

Basic Hooks in React - useEffect()

Basic Hooks in React - useEffect()

Subscribe to my newsletter and never miss my upcoming articles

This blog post continues the series about React Hooks.

Here we are exploring one of basic and very important hooks in React - useEffect().

What is useEffect()?

useEffect() is JavaScript function, which lets you perform side-effects in functional component.

Under side-effects we understand operations such as:

  • data fetching
  • subscriptions
  • manually changing the DOM etc.

They are called "side-effects" because can affect other components and can’t be done during rendering.

How and when use useEffect()

To use this hook, you need to import it first from React library like this:

import {useEffect} from 'react';

You can use this hook without import as well like this - React.useEffect(), for me it's just more convenient to import and destructure first this function and then use it when need in the code.

You use it in your functional component same as you would use life-cycle methods componentDidMount() and componentDidUpdate in class-based component :

// Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Perform necessary side-effects here
  });

For example, let's consider we are building an application which with get data from API (fetching data is a side-effect). Our app will get a list of articles about Redux and display it.

First step is writing our component and defining state - an array of articles in our case, which we are going to fetch from API. Than we simply display it on the page:

import { useState } from 'react';

const App = () => {

  const [data, setData] = useState(
     { articles:  [] });

  return (
    <ul>
      {data.articles.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}

export default App;

Second step is to use useEffect() and fetch the needed data inside this hook:

import { useState, useEffect } from 'react';
import axios from 'axios';

const App = () => {
  const [data, setData] = useState(
   { articles: [] });

  useEffect(async () => {
    const result = await axios(
      'https://hn.algolia.com/api/v1/search?query=redux',
    );

    setData(result.data);
  });

  return (
    <ul>
      {data.articles.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}

export default App;

Here useEffect() will fetch the data with axios from the API and set the data to the state of the component with the state hook's update function. The promise resolving happens with async/await.

But, if we run our app like it has written above, we are going to fall into an infinite loop. Why? Let's find out :)

Characteristics and features

useEffect() function can accept 2 parameters:

  • callback function (provides logic for side-effect)
  • dependencies array (provides list of dependencies of your side-effect: being props or state values.)

If dependencies array is omitted (like in our example above), then useEffect() will be invoked on every render. Why?

As written above, useEffect() behaves in functional component same as life-cycle methods componentDidMount() and componentDidUpdate() in class-based component.

That means that effect hook runs not only when component mounted, but also when component is being updated. In our example we update the state inside the hook right after we have fetched data, which means effect hook will run again because component was updated.

This is something we don't need, because we get our data only once the page is loaded and then we use it in app.

In our case we need that dependencies array to avoid the above-described situation and run only once, so we can re-write our effect hook like this:

useEffect(async () => {
    const result = await axios(
      'https://hn.algolia.com/api/v1/search?query=redux',
    );

    setData(result.data);
  }, [ ]);

So, to sum up about useEffect()dependencies array :

  1. Not provided: the side-effect runs after every rendering
import { useEffect } from 'react';

const MyComponent = () => {
  useEffect(() => {
    // Runs after EVERY rendering
  });  
}

2.An empty array []: the side-effect runs once after the initial rendering

import { useEffect } from 'react';

const MyComponent = () => {
  useEffect(() => {
    // Runs ONCE after initial rendering
  }, []);
}

3.Has props or state values [prop1, prop2, ..., state1, state2]: the side-effect runs only when any dependancies value changes.

import { useEffect } from 'react';

const MyComponent = ({ prop }) => {
  useEffect(() => {
    // Runs ONCE after initial rendering
    // and after every rendering ONLY IF `prop` changes
  }, [prop]);
}

You can use multiple useEffect() hooks in one component, each implementing its own logic. You can create customs hooks and use useEffect() inside it. Once you get more confidence and practice with this hook, you can do magic!

Resources used:

Thank you for reading my blog. Feel free to connect on LinkedIn or Twitter :)

Buy Me a Coffee at ko-fi.com

 
Share this