make setState in useEffect each time when screen appears - reactjs

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])

Related

How to properly resolve useEffect exhaustive dependencies when hooks used cause infinite rerenders when added to the dependencies

I have a component that needs to update when the url's query param is changed.
Currently this works fine, except that I get an exhaustive dependencies warning because I don't have the setQuery inside of the array. If I add it, I get an infinite loop.
What is the solution here to remove the warning?
import { useQueryParam, withDefault, StringParam } from 'use-query-params';
const [query, setQuery] = useQueryParam<string>(
'query',
withDefault(StringParam, ''),
);
useEffect(() => {
setQuery(query);
}, [query]); // adding setQuery will cause an infinite loop
Infinite loop is because:
useEffect will get invoked every time its dependencies change. Here dependency is query.
[query]);
Every time it gets invoked , we are setting (updating) query.
setQuery(query);
causing useEffect to invoke again.

UseEffect triggering without respect to dependency array

I have a function below which i used as an array dependency to a useEffect handler
const handleInputUpdate = (event) => {
const eventValue = event.target.value;
setState({ ...state, answer_text: eventValue, trigger: true })
// console.log("I am changing for no reason")
}
Below is the useEffect handler
useEffect(() => console.log(" I am changing for no reason in useeffect"), [handleInputUpdate])
What i want is the useEffect handler to run only when the handleInputUpdate function is called but it runs also on component mount.
Here's what i've observed
The handleInputUpdate function doesn't run on component mount but only i need it to
Without respect to the above observation, the useEffect handler runs anyway.
Here's what i've tried
I tried consoling a text inside the handleInputUpdate function to see whether it runs on component render but it doesn't.
Even though the function doesn't run, the useEffect handler triggers anyway which is not what i want.
How can i solve this ?
Thanks in advance
useEffect dependency array is not used to trigger the effect when a function is called; the elements of the array are observed for any change and then trigger the effect.
In this case, handleInputUpdate will change on every render because it is not memoised, so the effect will also run on every render.
Since handleInputUpdate changes the state when it is called, you are better off adding that state to your useEffect dependency array:
useEffect(() => {
if (answer_text && trigger) {
console.log("I am changing for a reason in useeffect")
}
}, [answer_text, trigger])
The handleInputUpdate function, while it doesn't run on render, looks like it's created when the component runs, just before rendering. Since it won't be === to the value last in the dependency array - the handleInputUpdate from the prior render - the effect callback will run.
You need to observe changes to the answer_text value in state instead.
useEffect(() => {
// ...
}, [state.answer_text]);
I would also recommend separating out your state into different variables - these aren't class components, don't feel like you have to mash everything together into a single object structure.
const [text, setText] = useState('');

React Useeffect running when page loads

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.

REST API inside React.useEffect to run on each refersh of page

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

React useEffect() Infinite loop with firebase and react-router

I've seen some answers say that to run useEffect only once, you have to give an empty list as second argument. This is my code
const history = useHistory();
useEffect(() => {
console.log('hi')
firebase.auth().onAuthStateChanged((user) => {
if (user)
history.push('/')
})
},[])
First the linter tells me that 'React Hook useEffect has a missing dependency: 'history'. Either include it or remove the dependency array'. I don't understand this because history is already removed since second argument is an empty array.
Second console logs hi infinitely. Why?
You cannot run a use effect hook only once if it uses a dependancy. Every time the dependancy changes it will force the hook to re-run.
You have to add history to the dependancy list because you are using the push method on history inside useEffect. This is non-negotiable.
useEffect(() => {
console.log('hi')
firebase.auth().onAuthStateChanged((user) => {
if (user)
history.push('/')
})
},[history])
You will have to switch back to classes and use the componentDidMount method if you want to fire a function with dependency only once.

Resources