Where can I make API call with hooks in react?
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/react@16.7.0-alpha.0/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
how to make api calls in reactjs new hook api and share that across components?
You can create a custom hook that creates a new piece of state advice
that you set when your network request has finished.
Return a function from the function given to useEffect
that cancels your request when the component that uses it re-renders or the component is unmounted. You can also give your url
in an array as the second argument to useEffect
to only re-run the network request when the url
actually changes.
Example
const { useState, useEffect } = React;
function useAdvice(url) { const [advice, setAdvice] = useState(null);
useEffect( () => { const timeout = setTimeout(() => { setAdvice(`Use ${url} to get some nice advice`); }, 1000); return () => clearTimeout(timeout); }, [url] );
return advice;}
function App() { const advice = useAdvice("https://google.com");
return <div>{advice}</div>;}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.production.min.js"></script><script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.production.min.js"></script><div id="root"></div>
React - API call within useEffect or not
Ideally,
if you want to make an API call on click of a button there is no need to use any hook like useEffect and useState, the way you did. your second approach is perfect and there is nothing wrong with that.
But if there is some specific usecase where it is required then there is nothing wrong in using hooks also you will just be making some extra rerenders but if that serves your use case then I guess that is fine too.
note:
<button onClick={setTrigger(true)}>Click me</button>
this is wrong, this setTrigger function will be call on mount of the component and I guess you might not want that.
you can instead do
<button onClick={()=>setTrigger(true)}>Click me</button>
OR
const MyComponent = () => {
const [myState, setMyState] = useState([])
const getData = async () => {
const callMyApi = await fetch('http://someApi.com')
setMyState(await callMyApi.json()
}
return (
<button onClick={getData}>Click me</button>
)
}
React, make a second API call based on a state set by a first api call
What you are missing is that when you are calling searchFollowing()
, React did not yet re-render, therefore location
wouldn't be updated. You could have an useEffect
that would listen to location
, and call searchFollowing()
there when location.lat
and location.lon
are defined:
useEffect(()=>{
if(location.lat && location.lon){
searchFollowing();
}
}, [location])
Then remove searchFollowing()
call inside search
function, as now you call it inside the above useEffect
.
Access API response data from a custom React hook
React doesn't guarantee that the state will be immediately updated after setState()
. This is because state updates are non-blocking which means that code execution within the click handler will not wait for the state update to finish. Even if you try using await
with the API_REQUEST()
, it will still not immediately reflect. State updates will be added to the task queue and the code will continue executing past this point, therefore when it reaches the if statement, the data will not be up to date at this point.
In old class component days, we could pass a callback to setState()
as a second argument which would allow us to do anything with upto date state.
this.setState(counter => counter + 1, updated => console.log(updated));
But with functional components, we can no longer do this and React docs recommend using effect hooks. Therefore, you have to implement a similar behavior yourself if you need it, by adding some kind of callback to your custom hook, that will be run when the API request is made. It could be done using useEffect()
inside your custom hook.
You can make the useEffect()
run only after data has changed by adding only data
as a dependency:
useEffect(() => {
}, [data]);
How to avoid useEffect()?
However, as you said you only want this to run on click and the above doesn't tell how the data actually changed. So we have to think outside the box here i.e. do we really need to take the data from the state? I think we can take the data by passing a callback as a second argument to API_REQUEST()
and bypass state:
useAxios.js
import { useEffect, useState } from "react";
const useAxios = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState(null);
const [controller, setController] = useState();
const API_REQUEST = async (config, callback) => {
const { AxiosInstance, method, url, requestConfig } = config;
try {
setLoading(true);
setError(null);
setData(null);
const ctrl = new AbortController();
setController(ctrl);
const response = await AxiosInstance({
method,
url,
...requestConfig,
signal: ctrl.signal
});
setData(response.data);
callback(response.data);
} catch (error) {
console.error({ error });
setError(error.message);
} finally {
setLoading(false);
}
};
useEffect(() => {
return () => controller && controller.abort();
}, [controller]);
return { loading, error, data, API_REQUEST };
};
export default useAxios;
Now you may use API_REQUEST()
like this:
API_REQUEST(
{
AxiosInstance: axios,
method: "get",
url: "/todos",
requestConfig: {}
},
(responseData) => console.log(responseData)
);
Here is a working example: https://codesandbox.io/s/api-request-callback-q40bxr
How to execute a function AFTER an API-Call was made with setState within useEffect?
You can just add control to your second useEffect, it will solve your issue. This is working sample https://codesandbox.io/s/heuristic-matan-3kdbmv
React.useEffect(() => {
if (allMemes.length > 0) {
getMemeImage();
}
}, [allMemes]);
Trying to make an API call before using setInterval() in useEffect() hook in React
the important thing to note here is that your updateData function should return a promise to make await work then your above logic will work perfectly. It will wait until the first API call is not finished before going to the second line.
useEffect(() => {
await updateData(id, state, setState);
const interval = setInterval(async () => {
if (id) {
await updateData(id, state, setState); // API call
}
}, 5000);
//update function would be like:
function updateData(id, state, setState) {
...
return API.get("/url");
}
}, []);
How to instantly display data after an API call in react hooks
You don't need stateCopy
, as you have it in the callback of the setState
:
const setInfo = async () => {
// we want to update the component only once
const results = await Promise.all(
state.map(item => getFetch(item.steamId))
);
// 's' is the current state
setState(s =>
results.map((res, index) => ({ ...s[index], date: res.Date })
);
};
Related Topics
What Is a Practical Use for a Closure in JavaScript
JavaScript Audio Play on Click
Check If Option Is Selected with Jquery, If Not Select a Default
Ignore Typescript Errors "Property Does Not Exist on Value of Type"
How to Add Text Inside the Doughnut Chart Using Chart.Js
What Are the Actual Uses of Es6 Weakmap
How to Understand Usecapture Parameter in Addeventlistener
Differencebetween & VS @ and = in Angularjs
Lodash - Difference Between .Extend()/.Assign() and .Merge()
Amazon S3 Direct File Upload from Client Browser - Private Key Disclosure
How to Sort an Array Without Mutating the Original Array
Typescript Export VS. Default Export
How to Submit a Form Using Phantomjs
How to Do Two-Way Filtering in Angularjs
Why Define an Anonymous Function and Pass It Jquery as the Argument