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
- JS loop for with condition getting 0 on javascript
- Instead of adding value i'm replacing the value of array
- Bundle error using webpack for Electron application `Cannot resolve module 'electron'`
- Even if a state has been changed, but the view has not been rendered again in React / Next.js
- Dockerizing a React App: The app starts inside the container, but it not accessible from the exposed port
- How do I manage state in react with a master-detail component
- MaterialUI Spinning loader help needed for React/Redux app
- Call method of functional component in a class
- push to array getting the file from firestore javascript
- React Native reset some property of state
- Using if else in reactjs render
- How do you send only the fields you want to update using the MERN stack?
- Property 'palette' is not recognised by DefaultTheme from MaterialUI, it stopped to work once material ui have been moved from v4 to v5
- Show highest score from list
- Should we use BEM(Block, Element, Modifier) in react styled component
- Can I integrate React into a Knockout JavaScript app?
- 3DS2 BrowserInfo data with React Native and Expo
- CSS animation on changing list order with React
- Can I mark code blocks as production or development only with webpack?
- Debouncing / throttling a callback in React using hooks without waiting for the user to stop typing to get the update
- Do I need these handmade components (React.Fragment that will passthrough props and conditionnal wrapper)
- how to set url in another input after upload file in reactjs
- Data manipulation (cleaning dataset before visualization) - Node.js
- Netlify redirection doesnt update my path
- Why is my text not changing as I'm writing text to the input tag in React
- Router in react-router-dom throws an error in the console with no message
- Radium ':Hover' not working
- How can i use React's fetch to store data into a variable?
- Reactjs onBlur-navbar
- Redux store takes lil bit time to update store which causes else block to execute and hence get redirected to unintended page