Generally speaking, using setState inside useEffect will create an infinite loop that most likely you don't want to cause. There are a couple of exceptions to that rule which I will get into later.
useEffect is called after each render and when setState is used inside of it, it will cause the component to re-render which will call useEffect and so on and so on.
One of the popular cases that using useState inside of useEffect will not cause an infinite loop is when you pass an empty array as a second argument to useEffect like useEffect(() => {....}, []) which means that the effect function should be called once: after the first mount/render only. This is used widely when you're doing data fetching in a component and you want to save the request data in the component's state.
Generally speaking, using setState inside useEffect will create an infinite loop that most likely you don't want to cause. There are a couple of exceptions to that rule which I will get into later.
useEffect is called after each render and when setState is used inside of it, it will cause the component to re-render which will call useEffect and so on and so on.
One of the popular cases that using useState inside of useEffect will not cause an infinite loop is when you pass an empty array as a second argument to useEffect like useEffect(() => {....}, []) which means that the effect function should be called once: after the first mount/render only. This is used widely when you're doing data fetching in a component and you want to save the request data in the component's state.
For future purposes, this may help too:
It's ok to use setState in useEffect . To do so, however, you need to ensure you don't unintentionally create an infinite loop.
An infinite loop is not the only problem that may occur. See below:
Imagine that you have a component Comp that receives props from parent and according to a props change, you want to set Comp's state. For some reason, you need to change for each prop in a different useEffect:
DO NOT DO THIS
useEffect(() => {
setState({ ...state, a: props.a });
}, [props.a]);
useEffect(() => {
setState({ ...state, b: props.b });
}, [props.b]);
It may never change the state of a , as you can see in this example:
https://codesandbox.io/s/confident-lederberg-dtx7w
The reason this occurs is that both useEffect hooks run during the same react render cycle. When props.a and props.b change at the same time, each useEffect captures the same stale state value from before the update. As a result, when the first effect runs, it calls setState({ ...state, a: props.a }) . Then, the second effect runs immediately after and calls setState({ ...state, b: props.b}) with the same stale state value, thereby overwriting the first update. The result is that a never appears to update. The second setState replaces the first one rather than merging the two updates together.
DO THIS INSTEAD
The solution to this problem is to call setState like this:
useEffect(() => {
setState(previousState => ({ ...previousState, a: props.a }));
}, [props.a]);
useEffect(() => {
setState(previousState => ({ ...previousState, b: props.b }));
}, [props.b]);
For more information, check the solution here: https://codesandbox.io/s/mutable-surf-nynlx
With this approach, you will always receive the most updated and correct value of the state.
I saw a guy get reemed in some code he shared because he was using useState in a useEffect.
I have some areas of my code where I do this.
For example I will use tanstack to fetch data. I’ll tie a useEffect to that data and setState so that data can be edited in a form.
How else would you do this?
**** Edit. I meant setState in useEffect.
reactjs - how can I set useState variable under useEffect? - Stack Overflow
Working of `useState` function inside `useEffect` function
useState in useEffect
If I shouldn't fetch in useEffect, where should I fetch?
Videos
I am walking through the code you have put here:
const [isMatched, setMatched] = useState(false)
You have set initial value of isMatched as false.
useEffect(()=>{
...
}, [isMatched])
Here the useEffect is registered as callback but not run yet.
console.log({isMatched})
Console logs false because useEffect has not run yet.
Then the return() JSX is rendered.
Now useEffect is called.
const aa = checkCondition() //aa gets checkCondition value
console.log({aa}) //aa is logged to console
setMatched(aa)//isMatched is set to aa's value, but this update will not happen immediately
console.log({isMatched})//This will output false(since initial value of isMatched is false), setState doesn't update immediately remember?
Each line explained above.
Now this useEffect has been given deps [isMatched], which means this code will run once in the beginning, then every time isMatched value changes. Now why would you want to do this, I don't know, I assume you saw the lint warning saying React Hook React.useEffect has a missing dependency: 'isMatched'. Either include it or remove the dependency array. (react-hooks/exhaustive-deps)eslint and put it there, which is wrong because you are only using it for a console.log. Either way, now whenever you call setMatched with a different value than it was previously(in this cycle previous value is false), it will rerun the useEffect callback.
This means if checkCondition output is true, useEffect will run once more, if it is false it will not. It will not fall into an infinite loop here as boolean values are primitive, so false===false and true===true comparisons are both true. If checkCondition output were a object reference it has the potential to fall into an infinite loop, because the isMatched dependency most likely would point to a different reference every time.
To avoid infinite loop as commented, update your state this way,
useEffect(()=>{
const aa = checkCondition()
setMatched(() => aa));
}, [])
setIsAuth doesn't cause the local variableisAuth to change its value. const data values can't be changed, and even if you defined it as let, that's not what setting state does. Instead, when you set state, the component rerenders. On that new render, the call to useState will return the new value, and you can use that new value for the new render.
The component renders for the first time. Then it runs the effect. The closure for the effect has the local variables from that first render, and your code uses those variables to log the false twice. Since you called set state, a new render will happen, and that new render will have different variables. When it's effect runs, it will log true twice, since that's the values in its closure.
Here are some comments in the code explaining how React setState will only update the local const once the component re-renders
import React, { useState, useEffect } from 'react';
const Authentication = () => {
// React will initialise `isAuth` as false
const [isAuth, setIsAuth] = useState(false);
useEffect(() => {
// Console log outputs the initial value, `false`
console.log(isAuth);
// React sets state to true, but the new state is not available until
// `useState` is called on the next render
setIsAuth(true);
// `isAuth` will remain false until the component re-renders
// So console.log will output `false` again the first time this is called
console.log(isAuth);
}, [isAuth]);
// The first time this is rendered it will output <p>False</p>
// There will be a re-render after the `setState` call and it will
// output <p>True</p>
return <div>{isAuth ? <p>True</p> : <p>False</p>}</div>;
};
export default Authentication;