简体   繁体   中英

React hooks state variable not updating after rerender

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> 

You 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM