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()
?
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:
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>
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.
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!