React钩子的奇怪行为:延迟数据更新

时间:2019-05-04 13:34:02

标签: javascript reactjs react-hooks

奇怪的行为:我希望第一和第二个console.log显示不同的结果,但是它们显示相同的结果,并且仅在下次单击时更改该值。我应该如何编辑代码,以使值有所不同?

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>

您可以在此处看到一个有效的示例: https://codesandbox.io/s/wz9y5lqyzk

3 个答案:

答案 0 :(得分:3)

setState钩子不会直接更新组件中的值。它会更新其内部状态,导致组件重新呈现,然后返回分配给count的新状态值。

当您在console.log(count)test时,将显示count的当前值,它是旧值(更新之前)。如果将console.log()移动到呈现器,则会看到新值:

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>

答案 1 :(得分:2)

State updates are asynchronous。从useState获得的setCount函数无法神奇地扩展和更改count常量的值。一方面,这是一个常数。另外,setCount无权访问。相反,当您调用setCount时,组件函数将再次被调用,而useState将返回更新后的计数。

实时示例:

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>

如果count更改时需要执行一些副作用,请使用useEffect

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

请注意,我们告诉useEffect仅在count发生更改时才调用回调,这样,如果我们处于其他状态,则回调不会不必要地运行。

实时示例:

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>

答案 2 :(得分:2)

状态更新是异步的,因此,如果您使用的是Hooks,则可以使用useEffect在更新后收到通知。

这里是一个例子:

https://codesandbox.io/s/wxy342m6l

如果您使用的是React组件的setState,则可以使用回调

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

如果需要状态值,请记住使用回调来更新状态。