Where can I make API call with hooks in react?

69,018

Solution 1

Yes, there's a similar (but not the same!) substitute for componentDidMount with hooks, and it's the useEffect hook.

The other answers don't really answer your question about where you can make API calls. You can make API calls by using useEffect and passing in an empty array or object as the second argument as a replacement for componentDidMount(). The key here is the second argument. If you don't provide an empty array or object as the second argument, the API call will be called on every render, and it effectively becomes a componentDidUpdate.

As mentioned in the docs:

Passing in an empty array [] of inputs tells React that your effect doesn’t depend on any values from the component, so that effect would run only on mount and clean up on unmount; it won’t run on updates.

Here are some examples for scenarios where you will need to make API calls:

API Call Strictly on Mount

Try running the code below and see the result.

function User() {
  const [firstName, setFirstName] = React.useState(null);
  const [lastName, setLastName] = React.useState(null);
  
  React.useEffect(() => {
    fetch('https://randomuser.me/api/')
      .then(results => results.json())
      .then(data => {
        const {name} = data.results[0];
        setFirstName(name.first);
        setLastName(name.last);
      });
  }, []); // <-- Have to pass in [] here!

  return (
    <div>
      Name: {!firstName || !lastName ? 'Loading...' : `${firstName} ${lastName}`}
    </div>
  );
}

ReactDOM.render(<User />, document.querySelector('#app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>

<div id="app"></div>

API Call Whenever Some Prop/State Changes

If you are for example displaying a profile page of a user where each page has a userID state/prop, you should pass in that ID as a value into the second parameter of useEffect so that the data will be refetched for a new user ID. componentDidMount is insufficient here as the component might not need remounting if you go directly from user A to user B's profile.

In the traditional classes way, you would do:

componentDidMount() {
  this.fetchData();
}

componentDidUpdate(prevProps, prevState) {
  if (prevState.id !== this.state.id) {
    this.fetchData();
  }
}

With hooks, that would be:

useEffect(() => {
  this.fetchData();
}, [id]);

Try running the code below and see the result. Change the id to 2 for instance to see that useEffect is run again.

function Todo() {
  const [todo, setTodo] = React.useState(null);
  const [id, setId] = React.useState(1);
  
  React.useEffect(() => {
    if (id == null || id === '') {
      return;
    }
    
    fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
      .then(results => results.json())
      .then(data => {
        setTodo(data);
      });
  }, [id]); // useEffect will trigger whenever id is different.

  return (
    <div>
      <input value={id} onChange={e => setId(e.target.value)}/>
      <br/>
      <pre>{JSON.stringify(todo, null, 2)}</pre>
    </div>
  );
}

ReactDOM.render(<Todo />, document.querySelector('#app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>

<div id="app"></div>

You should read up on useEffect so that you know what you can/cannot do with it.

Suspense

As Dan Abramov said on this GitHub Issue:

Longer term we'll discourage this (useEffect) pattern because it encourages race conditions. Such as — anything could happen between your call starts and ends, and you could have gotten new props. Instead, we'll recommend Suspense for data fetching

So stay tuned for Suspense!

Solution 2

You can use a library that provides the hooks for you like https://resthooks.io

Then getting your data becomes as simple as:

const article = useSuspense(ArticleResource.detail(), { id });

Now you grabbed the article by id. All non-happy paths (loading, error states) are handled by Suspense and Error boundaries respectively.

To get started follow this simple guide: https://resthooks.io/docs/getting-started/installation

At only 7kb gzipped this will save you a lot of pain and in the long run lower your bundle size due to less repeated code.

Demos

Solution 3

I am just posting this as a simpler way to understand acc. to my efforts. Credit to Yangshun Tay's post that it covers almost everything.

API call on mounting of component

Code:

  useEffect(() => { 
    // here is where you make API call(s) or any side effects
    fetchData('/data')
  }, [] ) /** passing empty brackets is necessary */

So using useEffect(fn,[]) with empty args as [] makes fn() triggered once throughout when component creates(mounts) and destroys(unmounts) without depending on any values.

Pro tip:

Also further if you return() something in this fn then it will act same as componentWillUnmount() lifecycle as that of class component.

  useEffect(() => { 
   fetchData('/data')
   return () => {
    // this will be performed when component will unmount
    resetData()
   }
  }, [] )

API call when some value changes

If you are looking to call API when some value changes, simply pass that variable (which is storing value) into arguments array in useEffect().

 useEffect(() => {
  // perform your API call here
  updateDetails();
 },[prop.name]) /** --> will be triggered whenever value of prop.name changes */

This will make sure that whenever value of prop.name changes, your function in hook gets triggered.

Also to note: this hook will be also called initially when the component is mounted. So at that time your name value might be in initial state which is kind of unintended from your view. So you can add custom condition in your function to avoid unnecessary API calls.

Solution 4

When you are using functional components with the hooks API, you can use the useEffect() method to produce side effects. Whenever the state is updated because of these side effects, the component will re-render.

Example from the docs.

import { useState, useEffect } from 'react';

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

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

For example, you could call setCount in a callback function of an async request. When the callback is executed, the state will get updated and React will re-render the component. Also from the docs:

Tip

If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.

Solution 5

You could also use use-http like:

import useFetch from 'use-http'

function App() {
  // add whatever other options you would add to `fetch` such as headers
  const options = {
    method: 'POST',
    body: {}, // whatever data you want to send
  }

  var [data, loading, error] = useFetch('https://example.com', options)

  // want to use object destructuring? You can do that too
  var { data, loading, error } = useFetch('https://example.com', options)

  if (error) {
    return 'Error!'
  }

  if (loading) {
    return 'Loading!'
  }

  return (
    <code>
      <pre>{data}</pre>
    </code>
  )
}
Share:
69,018
Hemadri Dasari
Author by

Hemadri Dasari

Founder @ www.letsdoretro.com Checkout Lets do retro Linkedin page. The Next generation collaborative retrospective tool. Where teams discuss what worked and what didn't. Together, online. Please do support by following our page. Checkout my Linkedin Profile

Updated on July 05, 2022

Comments

  • Hemadri Dasari
    Hemadri Dasari almost 2 years

    Basically we do API calls in componentDidMount() life cycle method in React class components like below

         componentDidMount(){
              //Here we do API call and do setState accordingly
         }
    

    But after hooks are introduced in React v16.7.0, its all like functional components mostly

    My query is, where exactly do we need to make API call in functional component with hooks?

    Do we have any method for it similar like componentDidMount()?

  • Husni Jabir
    Husni Jabir almost 5 years
    This is an excellent explanation on how to use reacts useEffect hook. But i believe this particular hook has a bug on componentDidMount life cycle method implementation as it has repetitive calls unless the second argument is provided.
  • Yangshun Tay
    Yangshun Tay almost 5 years
    @HusniJabir you mean my example is incorrect? Didn't I pass in an empty array as the second argument?
  • Husni Jabir
    Husni Jabir over 4 years
    I didn't mean that your example is incorrect, what I wanted to emphasize is if there is no second argument as empty array then the useEffect hook will run infinitely and that is a bug with the original hook implementation.
  • Yangshun Tay
    Yangshun Tay over 4 years
    I see, feel free to edit the answer to warn about that part :)
  • Glenn Mohammad
    Glenn Mohammad over 3 years
    Voted this up because of the given important note at the end: "... So you can add custom condition in your function to avoid unnecessary API calls." 👍
  • Organic
    Organic over 2 years
    This works but passing an empty array into useEffect it'll complain about any properties or methods you're using inside useEffect and aren't defined in the dependency array. "React Hook useEffect has a missing dependency: 'getData'. Either include it or remove the dependency array react-hooks/exhaustive-deps"
  • Yangshun Tay
    Yangshun Tay over 2 years
    @Organic Then include the dependencies? The example code here doesn't use a custom function to call the API so there's no need for dependencies. Thanks for the downvote :)
  • Organic
    Organic over 2 years
    If you include the dependencies in the array it doesn't just run once. It runs several times, sometimes on an infinite loop. Hooks cause more issues than they solve.
  • isherwood
    isherwood almost 2 years
    This has the distinct disadvantage of invoking twice when using Strict Mode in development. This results in duplicate ajax calls, key duplication errors, etc. This is hinted at in the final paragraph, but no real solution is provided here.