简体   繁体   中英

Strange behavior of React hooks: delayed data update

Strange behavior: I expect that the first and the second console.log display a different result, but they display the same result and the value is changed only on the next click. How should I edit my code so that the value will be different?

 function App() { const [count, setCount] = React.useState(false); const test = () => { console.log(count); //false setCount(!count); console.log(count); //false }; return ( <div className="App"> <h1 onClick={test}>Hello StackOverflow</h1> </div> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
 <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="root"></div>

You can see a working example here: https://codesandbox.io/s/wz9y5lqyzk

The state update is asynchronous so if you are using Hooks you can use useEffect to be notified after an update.

Here is an example:

https://codesandbox.io/s/wxy342m6l

If you are using setState of a React component, you can use the callback

this.setState((state) => ({count: !state.count}), () => console.log('Updated', this.state.count));

Remember to use the callback to update state if you need a state value.

State updates are asynchronous . The setCount function you get from useState can't magically reach out and change the value of your count constant. For one thing, it's a constant. For another, setCount doesn't have access to it. Instead, when you call setCount , your component function will get called again, and useState will return the updated count.

Live Example:

 const {useState} = React; function Example() { const [count, setCount] = useState(false); const test = () =>{ setCount(!count); // Schedules asynchronous call to Example, re-rendering the component }; return ( <div className="App"> <h1 onClick={test}>Hello CodeSandbox</h1> <div>Count: {String(count)}</div> </div> ); } ReactDOM.render(<Example />, document.getElementById("root"));
 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

If you need to perform some side-effect when count changes, use useEffect :

useEffect(() => {
  console.log(`count changed to ${count}`);
}, [count]);

Notice that we tell useEffect to only call our callback when count changes, so that if we have other state, our callback doesn't run unnecessarily.

Live Example:

 const {useState, useEffect} = React; function Example() { const [count, setCount] = useState(false); const test = () =>{ setCount(!count); // Schedules asynchronous call to Example, re-rendering the component }; useEffect(() => { console.log(`count changed to ${count}`); }, [count]); return ( <div className="App"> <h1 onClick={test}>Hello CodeSandbox</h1> <div>Count: {String(count)}</div> </div> ); } ReactDOM.render(<Example />, document.getElementById("root"));
 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

The setState hook doesn't update the value in the component directly. It updates it's internal state, causes a rerender of the component, and then returns the new state value, which you assign to count .

When you console.log(count) inside test , you display the current value of count , which is the old value (before the update). If you'll move the console.log() to the render, you'll see the new value:

 const { useState } = React; const Component = () => { const [count, setCount] = useState(false); const test = () => { console.log('before: ', count); setCount(!count) } console.log('after: ', count); return ( <div className="App"> <button onClick={test}>Click</button> </div> ); } ReactDOM.render( <Component />, demo );
 <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="demo"></div>

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