Why is React setState hook not updating immediately? [duplicate] - javascript

This question already has answers here:
Why does calling react setState method not mutate the state immediately?
(9 answers)
Closed 2 years ago.
I am trying to call a handler from a parent function with the updated state value as an argument, however, the state does not update immediately after the setSelected is called as both console logs are printing false(the initial value). After the onClick function is completed however, it gets updated.
onClick={() => {
console.log("Clicked: ", props.rank, props.suit, selected);
setSelected(!selected)
console.log("selected: ", selected)
// props.handle_card_selected(props.id, selected)
}}
useEffect(() => {
const check_border = () => {
if (selected) {
return "green"
}
return "black"
}
check_border()
}, [selected])

Two things you need to know about the state update in React:
State is updated asynchronously
In any particular render, state and props don't change; changes are only reflected after component re-renders
If you want to log the updated value of selected, put the log statement in the useEffect hook.
You can't update and log the updated value of any state variable in the same render; component will have to re-render to reflect changes due to state update.
Similarly, if you want to call a function with the updated value of selected, call the function from inside the useEffect hook.

setState is an async function which is why it doesn't update the state immediately. There is another way, i.e. pass function in setSelected, through which you can achieve the required behaviour:
onClick={() => {
console.log("Clicked: ", props.rank, props.suit, selected);
setSelected(prevSelected => {
// props.handle_card_selected(props.id, !prevSelected)
return !prevSelected
})
}}
useEffect(() => {
props.handle_card_selected(selected)
}, [selected, props.handle_card_selected])

Related

Hook useState with useEffect

I encountered this behavior. Two onChange are called in turn. One of them saves data to the state, and use the previus state to generate new one.
const onChange = (data) => {
setState1((prev) => {
console.log("onChange set state");
return [...prev, data];
});
};
The other just prints something to the console.
const onChange2 = (data) => {
console.log("onChange2");
};
onChange and onChange2 called after click on button
<button
onClick={() => {
onChange({ foo: "bar" });
onChange2();
}}
>
2
</button>
useEffect - saves data to state
const [state, setState] = useState(0);
const [state1, setState1] = useState([]);
useEffect(() => {
setState((prev) => {
console.log("use effect set state");
return prev + 1;
});
}, []);
So, if you do not use useEffect (comment out), the console log, which in the first onChange will be called first, then the console log from the second onChange
If you uncomment useEffect, then the console will log from onChange2 first, then from onChange
Why is this happening, who can tell?
CodeSandBox: https://codesandbox.io/s/eager-hooks-sczvb
The call order of onChange and onChange2 appear to change because the console.log in onChange is inside the callback passed to setState1.
If you move the console.log directly in the onChange you will see that the order is the same with or without useEffect.
const onChange = (data) => {
console.log("onChange set state");
setState1((prev) => {
return [...prev, data];
});
};
It's important to understand that when you call setState there is no guarantee that the state will be changed immediately, it's up to React to decide when to apply the new state and trigger a new render.
This is why the callback passed to setState should only return a new state and not run any effects (such as console.log) since the moment this code is run is considered in implementation detail and could change with any version of React.
Expected behavior is onChange2 called first, and onChange set state called second whatever useEffect body is commented. because onChange2 called right now. but onChange set state in setState fn called after rendered。
The "bug" looked is why onChange set state called first when first click. Hmm... find react computeExpirationForFiber. setState callback execute sync or async by different conditions. It's hard to read.

Function gets automatically called without changing the useffect value

Hi I am using redux in react application and I am using redux hooks with functional components.The problem is I want to call this function and dispatch the action only when the value of unmount gets changed.But it's not working as expected it's calling this function automatically.
const checkHasMessages = () => {
recentUSers.map((ci) => {
if (!ci.hasOwnProperty("mesg")) {
dispatch(removeRecentUser(ci.id));
}
});
};
useEffect(() => {
checkHasMessages();
}, [unmount]);
On component mount the state unmout is being set to undefined, thus triggering the useEffect with eh dependency.
You can add an if statement to check the value of unmount.
useEffect(() => {
if(unmount){
checkHasMessages();
}
}, [unmount]);
This should do it (if unmount is initially set to undefined, else u need to create a check based of the value)

How can I update an obect to the value of another object with the same structure? [duplicate]

This question already has answers here:
Why does calling react setState method not mutate the state immediately?
(9 answers)
Closed 2 years ago.
I am trying to call a handler from a parent function with the updated state value as an argument, however, the state does not update immediately after the setSelected is called as both console logs are printing false(the initial value). After the onClick function is completed however, it gets updated.
onClick={() => {
console.log("Clicked: ", props.rank, props.suit, selected);
setSelected(!selected)
console.log("selected: ", selected)
// props.handle_card_selected(props.id, selected)
}}
useEffect(() => {
const check_border = () => {
if (selected) {
return "green"
}
return "black"
}
check_border()
}, [selected])
Two things you need to know about the state update in React:
State is updated asynchronously
In any particular render, state and props don't change; changes are only reflected after component re-renders
If you want to log the updated value of selected, put the log statement in the useEffect hook.
You can't update and log the updated value of any state variable in the same render; component will have to re-render to reflect changes due to state update.
Similarly, if you want to call a function with the updated value of selected, call the function from inside the useEffect hook.
setState is an async function which is why it doesn't update the state immediately. There is another way, i.e. pass function in setSelected, through which you can achieve the required behaviour:
onClick={() => {
console.log("Clicked: ", props.rank, props.suit, selected);
setSelected(prevSelected => {
// props.handle_card_selected(props.id, !prevSelected)
return !prevSelected
})
}}
useEffect(() => {
props.handle_card_selected(selected)
}, [selected, props.handle_card_selected])

react select onChange returning previous value instead of current

I'm attempting to update the state of a Parent component after the user updates the value of a Child component's select element.
Whilst I've got it somewhat working, I've noticed that when I fire the onChange event on my select, it will always return the previous value instead of the value that has just been selected.
class Parent extends Component{
constructor(props){
super(props);
this.state = { data: {
condition: "any"
}};
}
update = data => {
this.setState({ data: {
...this.state.data,
...data
}});
// This gets called every time I change the value of my select element.
console.log(this.state.data);
}
render(){
return (
<Child
condition={this.state.data.condition}
onUpdate={data => this.update(data)} />
);
}
}
class Child extends Component{
updateParent = data => {
this.props.onUpdate(data);
}
render(){
const options = [
["any", "Any Condition"],
["new", "Brand New"],
["used", "Used"]
];
return (
<select
defaultValue={ props.selected }
onChange={({ target }) => this.updateParent({ condition: target.value })}>
{ options.map(([id, name]) => (
<option key={id} value={id}>{ name }</option>
)}
</select>
);
}
}
In this example, if I select used, the console will return any. Then, if I select new, the console will return used.
setState operations are async in nature. So whenever a setState op is done, its not guaranteed that the updated value of state will be available just after the setState
statement
From React Doc
React may batch multiple setState() calls into a single update for
performance.
Because this.props and this.state may be updated asynchronously, you
should not rely on their values for calculating the next state.
Now, if you want to use the new state value, you should store the value, in this case the data, in a variable, set your state, but use the variable to perform other operation inside the function, like calling API,etc.
Edit (As pointed out by #Grover):
setState also provides a second argument which is a callback that gets fired after the update operation takes place. One can get the updated state value in it and can use this to perform operations with the updated values.
this.setState({foo: 'bar'}, () => {
// actions
});
However, the React Doc suggests using componentDidUpdate instead of setState callback. This answer tries to explain it why: What is the advantage of using componentDidUpdate over the setState callback?

React useState with addEventListener

Can someone explain to me exactly why the callback from addEventListener after changing value (by click button) in useState from "init" to "new state" shows the value "init" while text in button is "new state"
function Test() {
const [test, setTest] = useState("init")
useEffect(() => {
window.addEventListener("resize", () => console.log(test))
}, [])
return (
<button style={{ marginTop: "100px" }} onClick={() => setTest("new state")}>
{test}
</button>
)
}
Add test to the dependency array in your useEffect:
useEffect(() => {
const func = () => console.log(test);
window.addEventListener("resize", func)
return () => {
window.removeEventListener("resize", func)
}
}, [test])
An explanation of why this is needed is covered in the Hooks FAQ: Why am I seeing stale props or state inside my function?:
another possible reason you’re seeing stale props or state is if you
use the “dependency array” optimization but didn’t correctly specify
all the dependencies. For example, if an effect specifies [] as the
second argument but reads someProp inside, it will keep “seeing” the
initial value of someProp. The solution is to either remove the
dependency array, or to fix it.
Empty dependency array acts as componentDidMount. You should add test as one of the dependencies so it fires again
function Test() {
const [test, setTest] = useState("init")
useEffect(() => {
window.addEventListener("resize", fun)
return () => {
window.removeEventListener("resize", fun)
}
}, [test])
return (
<button style={{ marginTop: "100px" }} onClick={() => setTest("new state")}>
{test}
</button>
)
}
Another reason this is happening is the functional component is using stale state value.
Event is registered once on component mount with useEffect. It's the same function during entire component lifespan and refers to stale state that was fresh at the time when this eventListener was defined the first time.
The solution for that is using refs
Check out https://codesandbox.io/s/serverless-thunder-5vqh9 to understand the solution better
State inside an effect fired once is never updated (if you use [])
The value of "test" inside the scope of your useEffect is never updated since the effect was fired only once at mount.
The State changes you make afterwards will never change this value unless you fire the Effect again but passing an argument into the square brackets.

Categories

Resources