score:39
Effects are always executed after the render phase is completed even if you setState inside the one effect, another effect will read the updated state and take action on it only after the render phase.
Having said that its probably better to take both actions in the same effect unless there is a possibility that b
can change due to reasons other than changing a
in which case too you would want to execute the same logic
score:2
Try wrapping the setState inside an if-statement that checks whether the state needs to be changed - if yes, change it, else return () => {}
e.g.,
useEffect(() => {
if(a.currentCondition !== a.desiredCondition) {
setA();
}
return cleanup;
}, [b])
score:24
▶ 1. Can I set state inside a useEffect hook?
In principle, you can set state freely where you need it - including inside useEffect
and even during rendering. Just make sure to avoid infinite loops by settting Hook deps
properly and/or state conditionally.
▶ 2. Lets say I have some state that is dependent on some other state. Is it appropriate to create a hook that observes A and sets B inside the useEffect hook?
You just described the classic use case for useReducer
:
useReducer
is usually preferable touseState
when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. (React docs)When setting a state variable depends on the current value of another state variable, you might want to try replacing them both with
useReducer
. [...] When you find yourself writingsetSomething(something => ...)
, it’s a good time to consider using a reducer instead. (Dan Abramov, Overreacted blog)
let MyComponent = () => {
let [state, dispatch] = useReducer(reducer, { a: 1, b: 2 });
useEffect(() => {
console.log("Some effect with B");
}, [state.b]);
return (
<div>
<p>A: {state.a}, B: {state.b}</p>
<button onClick={() => dispatch({ type: "SET_A", payload: 5 })}>
Set A to 5 and Check B
</button>
<button onClick={() => dispatch({ type: "INCREMENT_B" })}>
Increment B
</button>
</div>
);
};
// B depends on A. If B >= A, then reset B to 1.
function reducer(state, { type, payload }) {
const someCondition = state.b >= state.a;
if (type === "SET_A")
return someCondition ? { a: payload, b: 1 } : { ...state, a: payload };
else if (type === "INCREMENT_B") return { ...state, b: state.b + 1 };
return state;
}
ReactDOM.render(<MyComponent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
<script>var { useReducer, useEffect } = React</script>
▶ 3. Will the effects cascade such that, when I click the button, the first effect will fire, causing b to change, causing the second effect to fire, before the next render?
useEffect
always runs after the render is committed and DOM changes are applied. The first effect fires, changes b
and causes a re-render. After this render has completed, second effect will run due to b
changes.
let MyComponent = props => {
console.log("render");
let [a, setA] = useState(1);
let [b, setB] = useState(2);
let isFirstRender = useRef(true);
useEffect(() => {
console.log("useEffect a, value:", a);
if (isFirstRender.current) isFirstRender.current = false;
else setB(3);
return () => {
console.log("unmount useEffect a, value:", a);
};
}, [a]);
useEffect(() => {
console.log("useEffect b, value:", b);
return () => {
console.log("unmount useEffect b, value:", b);
};
}, [b]);
return (
<div>
<p>a: {a}, b: {b}</p>
<button
onClick={() => {
console.log("Clicked!");
setA(5);
}}
>
click me
</button>
</div>
);
};
ReactDOM.render(<MyComponent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
<script>var { useReducer, useEffect, useState, useRef } = React</script>
▶ 4. Are there any performance downsides to structuring code like this?
Yes. By wrapping the state change of b
in a separate useEffect
for a
, the browser has an additional layout/paint phase - these effects are potentially visible for the user. If there is no way you want give useReducer
a try, you could change b
state together with a
directly:
let MyComponent = () => {
console.log("render");
let [a, setA] = useState(1);
let [b, setB] = useState(2);
useEffect(() => {
console.log("useEffect b, value:", b);
return () => {
console.log("unmount useEffect b, value:", b);
};
}, [b]);
const handleClick = () => {
console.log("Clicked!");
setA(5);
b >= 5 ? setB(1) : setB(b + 1);
};
return (
<div>
<p>
a: {a}, b: {b}
</p>
<button onClick={handleClick}>click me</button>
</div>
);
};
ReactDOM.render(<MyComponent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
<script>var { useReducer, useEffect, useState, useRef } = React</script>
score:35
useEffect
can hook on a certain prop or state. so, the thing you need to do to avoid infinite loop hook is binding some variable or state to effect
For Example:
useEffect(myeffectCallback, [])
above effect will fire only once the component has rendered. this is similar to componentDidMount
lifecycle
const [something, setSomething] = withState(0)
const [myState, setMyState] = withState(0)
useEffect(() => {
setSomething(0)
}, myState)
above effect will fire only my state has changed this is similar to componentDidUpdate
except not every changing state will fire it.
You can read more detail though this link
score:128
For future purposes, this may help too:
It's ok to use setState in useEffect
you just need to have attention as described already to not create a loop.
But it's 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 why this happen in this example it's because both useEffects run in the same react cycle when you change both prop.a
and prop.b
so the value of {...state}
when you do setState
are exactly the same in both useEffect
because they are in the same context. When you run the second setState
it will replace the first setState
.
DO THIS INSTEAD
The solution for this problem is basically call setState
like this:
useEffect(() => {
setState(state => ({ ...state, a: props.a }));
}, [props.a]);
useEffect(() => {
setState(state => ({ ...state, b: props.b }));
}, [props.b]);
Check the solution here: https://codesandbox.io/s/mutable-surf-nynlx
Now, you always receive the most updated and correct value of the state when you proceed with the setState
.
I hope this helps someone!
score:188
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.
Source: stackoverflow.com
Related Query
- Can I set state inside a useEffect hook
- Can i set state in parent from child using useEffect hook in react
- cannot set state inside useEffect hook
- How can I dynamically set the type of state inside a custom hook
- Can not update state inside setInterval in react hook
- useState hook can only set one object at the time and return the other object of the same array to initial state
- Access old state to compare with new state inside useEffect react hook with custom hooks usePrevious
- Invalid hook call. Hooks can only be called inside of the body of a function component when i call useQuery in useEffect
- Can I use the UseEffect hook inside getStaticProps?
- React Hooks: using useState inside useEffect doesn't set the state immediately after first rendering
- Set a state from useState without triggering useEffect hook
- How to set state in useEffect hook using React and Typescript?
- React cannot set state inside useEffect does not update state in the current cycle but updates state in the next cycle. What could be causing this?
- Graphql subscriptions inside a useEffect hook doesn't access latest state
- UseQuery graphql -> useState with useEffect such that I can run second useEffect to set more state
- How to set order of functions execution inside useEffect hook
- React - How to use the current state value as a variable so i can set as a index inside an array
- How do I update state with a button click when initial state is set in an useEffect hook api call?
- State variable inside a function defined within useEffect hook is not updating when value changes
- Set state for a hook functional component inside another functional component
- useState set method inside useEffect hook not updating till refreching the page
- React Hooks: Instantiating state hooks on validation Error: Invalid hook call. Hooks can only be called inside of the body of a function component
- State not getting set inside fetch inside useeffect
- Error: Invalid hook call. Hooks can only be called inside of the body of a function component. When using useEffect
- How can I map a prop array that has been set in the useEffect hook
- cannot set state inside useEffect
- React - State set inside useEffect axios request is empty
- Value of state variable different inside useEffect hook (firestore onSnapshot callback)
- React useEffect Invalid hook call. Hooks can only be called inside of the body of a function component
- Fixing stale state inside useEffect Hook
More Query from same tag
- How to change image on button click with React Hooks?
- React - using nested objects as state with hooks to fill form data
- Confused about how to create a ref if there is no forward ref?
- How can I show several different divs in the same popup?
- Upload Files via JS to ASP.Net Core with IFormFile
- How to use react-router 4.0 to refresh current route? not reload the whole page
- How to get onClick in React to work on a single element with multiple siblings?
- Cant get the value of <li> tag in my handler function
- Cant select radio button when one is already checked
- Autoselect text in input - Reactjs
- How to access object property by variable in Javascript
- useEffect cleanup never called when using Electron
- React how to test out react compoennt with setTimeout
- Material-UI Dialog style action button
- How to access typescript type keys?
- Rendering <li></> with array.map not working: Return statement in map returning undefined
- React: accessible unordered list of buttons
- Not sending unchanged input fileds with react hook form
- React: component subscribing to redux store changes
- Why isn't my state correctly mapped to props in Redux?
- useEffect renders more than once even with empty dependency list
- React with Apollo returns process is not defined
- How to change Font Awesome props with respect to media queries in React.js?
- Dynamically format string using regex
- What exactly does "react-native link" do?
- Event-driven approach in React?
- Requiring React components in my html files
- Validation on inputs in react js
- React chartjs 2 - Type 'string' is not assignable to type '"timeseries"'
- React: I'm not able to access the images folder from pages folder