In the following example I have an array of element (fruits) that I want to update and use the updated array to perform other operations (in this specific case saving the updated list). My understanding was on re rerender the state will update...but it does not here..or there is a delay between state updating and my action.
In the addFruit
function I can see 'Peach' being immediately added but the console from the saveFruits
function still shows the state fruit
remains unchanged. How can I have this updated to use immediately. (And I know in this case I can pass newFruits
variable to saveFruits
to update but I need fruits
to be updated for other places as well).
Forgive me because this is my millionth question about react async states but there's something just not clicking for me yet.
const { useState } = React;
function ParentComp() {
const[fruits, setFruits] = useState(['Apple', 'Orange', 'Banana', 'Pomegranate', 'Kiwi'])
const addFruit = () => {
let newFruits = Object.assign([], fruits)
newFruits.push('Peach')
setFruits(newFruits)
saveFruits()
}
const saveFruits = () => {
console.log('API req to save fruits.', fruits)
}
return (
<div>
{ fruits.map( (fruit, key) => <div key={key}>{fruit}</div> ) }
<button type="button" onClick={addFruit}>Add Peach</button>
</div>
)
}
ReactDOM.render(<ParentComp />, document.getElementById('root'))
<script src="@16/umd/react.development.js" crossorigin></script>
<script src="@16/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
In the following example I have an array of element (fruits) that I want to update and use the updated array to perform other operations (in this specific case saving the updated list). My understanding was on re rerender the state will update...but it does not here..or there is a delay between state updating and my action.
In the addFruit
function I can see 'Peach' being immediately added but the console from the saveFruits
function still shows the state fruit
remains unchanged. How can I have this updated to use immediately. (And I know in this case I can pass newFruits
variable to saveFruits
to update but I need fruits
to be updated for other places as well).
Forgive me because this is my millionth question about react async states but there's something just not clicking for me yet.
const { useState } = React;
function ParentComp() {
const[fruits, setFruits] = useState(['Apple', 'Orange', 'Banana', 'Pomegranate', 'Kiwi'])
const addFruit = () => {
let newFruits = Object.assign([], fruits)
newFruits.push('Peach')
setFruits(newFruits)
saveFruits()
}
const saveFruits = () => {
console.log('API req to save fruits.', fruits)
}
return (
<div>
{ fruits.map( (fruit, key) => <div key={key}>{fruit}</div> ) }
<button type="button" onClick={addFruit}>Add Peach</button>
</div>
)
}
ReactDOM.render(<ParentComp />, document.getElementById('root'))
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
Share
Improve this question
asked Apr 8, 2019 at 21:26
bos570bos570
1,5234 gold badges25 silver badges49 bronze badges
1
- 4 SetState is done asynchronously. Logging to console will not show the final value in state. – Eran Goldin Commented Apr 8, 2019 at 21:33
2 Answers
Reset to default 12You can try with useEffect which will give updated state whenever there will be update.
useEffect(() => {
saveFruits();
}, [fruits]);
It's not accurate to console.log fruits
because the reference to fruits
is still referring to the fruits
you had before you called setFruits
.
If we console.log newFruits
, which has the change, it would be more accurate of what is happening.
EDIT: It's probably better to useEffect
as what @Atul suggested though.
It sometimes helps to visualize how this is done in old React classes. In old React classes the equivalent of this is this (to some degree not actually but close enough to illustrate the point) (Read more: https://reactjs.org/docs/hooks-state.html)
class ParentComp extends React.Component {
constructor() {
super();
this.state = {
fruits: ['Apple', 'Orange', 'Banana', 'Pomegranate', 'Kiwi']
};
}
addFruit = () => {
let newFruits = Object.assign([], this.state.fruits);
newFruits.push('Peach')
this.setFruits(newFruits)
this.saveFruits();
}
setFruits = (fruits) => {
this.setState({
fruits
});
}
saveFruits = () => {
console.log(this.state.fruits);
}
render() {
return (
<div>
{this.state.fruits.map((fruit, key) => {
return (<div key={key}>{fruit}</div>) })}
<button type="button" onClick={this.addFruit}>Add Peach</button>
</div>
);
}
}
ReactDOM.render(<ParentComp /> , document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
We get the same problem above but it might be a little clearer. The setState for fruits
does get called but the console.log happens before the state/render change happens so this.state.fruits
is still referring to what was in the state before.
I highly recommend reading React hooks: not magic, just arrays to get a better sense of what goes on behind the scenes of hooks. It helps explain it a lot.