I have put a RESTFUL API inside my react useeffect hook by using axios.get() method, I need this REST API function is fetched and run on each refresh of the page?
Actually when, I am testing my application it is jus running once and no longer it updates
My react useeffect is like below
React.useEffect(() => {
window.scrollTo(0, 0);
document.body.scrollTop = 0;
axios.get('http://127.0.0.1:9000/api/is_logged_in/')
.then(res =>{
console.log(res);
if (res.status!=200) {
throw new Error('Network response was not ok');}
return res;})
.then(res=>{
const value=res.data.res;
set_is_logged_in(value);
}).
catch(error=>{
console.log(error);
});});
I need this API to get re-run and fetched from the sever on each refresh of the page. How to achieve such functionality in reactjs?
Try to understand the concept:
useEffect(() => {
// your logic here which will run only one time when this component mounted
});
useEffect(() => {
// your logic here which will run every time when the variable which is passed in dependency array change
}, []); // Blank Dependency array
useEffect(() => {
// your logic here which will run every time when the variable which is passed in dependency array change
}, [variable1, variable2]); // Dependency array
Explanation:
Giving it an empty array acts like componentDidMount as in, it only
runs once.
Giving it no second argument acts as both componentDidMount and
componentDidUpdate, as in it runs first on mount and then on every
re-render.
Giving it an array as second argument with any value inside, eg ,
[variable1] will only execute the code inside your useEffect hook
ONCE on mount, as well as whenever that particular variable
(variable1) changes.
Reference
Related
Overview
The below functional component renders the list of users in a dropdown. In the edit mode, we can change the current user to another user from the list of users.
Expected
The given code is fetching details thrice. I need to reduce it to one.
function UsersListView(props){
const { edit } = props // this is true for this case.
const refreshUsers = useRef(true);
const [error,users,refersh]=useFetchData('/profiles')
useEffect(() => {
if(edit && refreshUsers.current){
const fetchData = async () => {
await refresh();
}
fetchData();
refreshUsers.current = false;
}
},[edit, refresh])
return (
...... JSX CODE TO RENDER USERS
)
}
In the above code, we are making 3 API calls.
In the initial render
When we refresh the data => We fetch the data again. => API called
As refresh is in the dependency of useEffect() hook a re-render occurs then calling useFetchData() => API called.
My attempt
Initially refershUsers.current=false is under await refresh() which got me 8 API calls.
The above-mentioned code still gives me 3 API calls.
I tried moving const [error,users,refersh]=useFetchData('/profiles') inside useEffect() which throws an error. Hooks can't be used inside callbacks.
Queries
Is there a way to reduce the API calls to 1.
Why were the API calls reduced from 8 to 3 when I moved the line refershUsers.current=false outside
Try using a separate useEffect for each dependency. React allows you to use multiple useEffect within the same component.
one of them could be with an empty dependency array for the initial fetch. and then update state or make additional fetch calls as needed in another useEffect
Am using useEffect in a react functional component to fetch data from an external API but it keeps calling the API endpoint on render on the page .
Am looking for a way to stop the useeffect from running on render on the component
Use the dependency array (second argument to the useEffect), so you can specify when you need to run the useEffect.
The problem here is that you have not used the dependency array, so that it executes every time. By adding a dependency array, you specify the changes where you want useEffect to run.
useEffect(()=>{
},[<dependency array: which contains the properties>]);
If you leave the dependency array empty, it will run only once. Therefore if you want the API call to run only once, add an empty array as the second argument to your useEffect. This is your solution.
Like this:
useEffect(()=>{
//Your API Call
},[]);
useEffect is always meant to run after all the changes or render effects are update in the DOM. It will not run while or before the DOM is updated. You may not have given the second argument to useEffect, which if u do not provide will cause the useEffect to execute on each and every change. Assuming you only want to call the API just once when on after the first render you should provide an empty array.
Runs on all updates, see no second argument to useEffect:
useEffect(() => { /* call API */ });
Runs when the prop or state changes, see the second argument:
useEffect(() => { /* call API */ }, [prop, state]);
Runs only once, see the empty second argument:
useEffect(() => { /* call API */ }, []);
I recommend you to read the full documentation about the React useEffect hook.
Here is a easy example of using useEffect
function functionalComponent() {
const [data, setData] = React.useState(null);
React.useEffect(() => {
const url = 'https://randomuser.me/api/?results=10';
fetch(url)
.then(data => {
setData(data);
})
.catch(error => console.error(error))
}, []); // it's necessary to use [] to avoid the re-rendering
return <React.Fragment>
{data !== null && (
<React.Fragment>
{data.results.map(data => (
<div>
{data.gender}
</div>
))}
</React.Fragment>
)}
</React.Fragment>;
}
Maybe in your useEffect implementation you are avoiding the [] dependencies, this is a bit hard to understand if you come from class states. This on hooks review when a state element inside the hook change, for example if you are using an element that always change like a prop that you pass throught another component you might be setting inside the dependencies or another state, if you do not need any dependency just use it empty like the example above. As you can see in the documentation sometimes the dependencies are not used, this might generate an infinite loop.
i have some useEffect function which works when component appeared at first time:
useEffect(() => {
(async function () {
try {
console.log('works');
const token = await AsyncStorage.getItem('token');
if (token)
setState({...state, isAuthorized: true})
} catch (error) {
console.log(error)
}
})();
}, []);
But how to make it work every time without calling infinite loop, if i remove the empty array from the parameters?
You have to mention the dependency array as the second parameter to useEffect() hook to tell React when you need the useEffect to run. The default case is that react will run useEffect on every component render/update.
So, if you are running into an infinite loop, its likely that your dependency array is not proper. You are essentially running into case where your useEffect will run, update something in your component, this will trigger a re-render, and then useEffect will run again and this cycle will keep repeating infinitely.
I don't think of any use case where you wouldn't mention the dependency array when you are using the useEffect hook. It would always be advisable to mention the list of dependencies as an array to the second parameter of useEffect hook or if there are no any dependencies, then you must pass an empty array. This should prevent infinite loop/re-renders from happening in your application.
You can pass the value as the second argument so it will be rendered when that value changes. for example:
useEffect(() => {}, [state])
const [ countries, setCountries ] = useState([])
const hook = () => {
axios
.get('https://restcountries.eu/rest/v2/all')
.then(response => {
setCountries(response.data)
})
}
useEffect(hook, [])
This one below doesn't work:
//Uncaught TypeError: Cannot read property 'name' of undefined
console.log(countries[1].name)
This one below does work:
<ul>
{countries.map(country => (
<li>{country.name}</li>
))}
</ul>
Any ide why one method of printing name does work, while the other doesn't?
Coz you can loop through the empty array, but you can't access the index which is not available yet
// So if
countries = []
// this will not throw error
{countries.map(country => (
<li>{country.name}</li>
))}
// but this will
console.log(countries[1].name)
// if you want to check try to run this
console.log(countries.length ? countries[1].name : "not available yer");
The usage of useEffect hook notifies React that component has to perform some side-effects(passed as a callback function to the hook) after it has been rendered, The default behavior of useEffect will run both after the first render and after every update, but when an empty array is passed as a dependency the side-effect will be performed only once after the component has been mounted for the first time.
In the case above useEffect(hook, []) the callback hook will be called after the component has mounted for the first time, which means the component will render with the initial state on it's first render which is an empty array ([]).
That is why when you try to access countries[1].name it errors out, because the value of countries is still an empty array on the first render.
const [ countries, setCountries ] = useState([])
const hook = () => {
axios
.get('https://restcountries.eu/rest/v2/all')
.then(response => {
setCountries(response.data)
})
}
useEffect(hook, [])
// can not use index expression to get the first element because
// the value of countries is still an empty array on first render
// it only gets populated when axios.get call is succesful inside the
// callback in useEffect hook after the component has mounted for the first time
console.log(countries[1].name)
Solution
Check for the length of the array before trying to get the first element,
if (countries.length) {
console.log(countries[1].name)
}
P.S.- You should be using a .catch block for handling the error when the API call fails.
There is an example solution for a type of request like this in the React document:
https://reactjs.org/docs/hooks-effect.html
The hooks provided by React are for the most part, asynchronous functions provided by React, to help manage the loading of data, presenting it to the DOM, and dealing with updates. The useEffect behaves in a similar way to componentHasLoaded, where the hook is triggered once the functional component has rendered, and the DOM has been loaded, but it may not have been presented to the user yet. It's important to remember this when working with useEffect. useState is another asynchronous hook, but it provides access to the state property of the hook after it has been instantiated, and won't immediately trigger a re-render of the component, unless the data is updated.
The reason you get an undefined error when you attempt to access console.log(countries[1].name) is because the array at that point is still empty.
I'll explain in code:
const myComponent = () => {
// initialise countries: []
const [ countries, setCountries ] = useState([])
const hook = () => {
axios
.get('https://restcountries.eu/rest/v2/all')
.then(response => {
// This is allow you to see the DOM change after the effect has run
setTimeout(() => setCountries(response.data), 5000);
})
}
// Tell react to run useEffect once the component is loaded
useEffect(hook, [])
// Display data
return (
<p>Countries: {countries.length}<p>
);
};
Because useEffect is an asynchronous function, it doesn't block the execution of the function and the rendering of the DOM, but refreshes the DOM once useEffect is completed. In this case, you are setting the country list, based on the result of the useEffect function.
The useEffect function will still trigger, you will have access to the state, and the function will re-render when the state is updated.
See codepen example:
https://codepen.io/jmitchell38488/pen/OJMXZPv
I have an application where I am fetching the store as such:
const user_data = useSelector(state => state.user_data);
I have a useEffect() function right after that that assigns certain state objects to the data received from the store.
useEffect(() => {
setName(user_data.name);
}, []);
The store is being set by something like:
await dispatch(save_user_info(res.data.response));
props.history.push("/setting/dashboard");
Now the problem is, user_data is empty when useEffect first runs.
It gets populated afterwards, but useEffect just runs once.
The page is redirected to the new component before the store is updated, so when the page loads first, it does not have the store data.
How can I make useEffect run again if the data from the store is returned?
if you keep the 2nd argument of useEffect empty it will run every time the component updates.
useEffect(() => {
setName(user_data.name);
});
in your case it runs if variables mentioned in this array [](which is empty) are changed.
or you can do this
useEffect(() => {
setName(user_data.name);
},[user_data]); // or props.user_data , howsoever you can refer to that