使用 useState 钩子了解 reactjs 中奇怪的 setState 行为

IT技术 reactjs react-hooks state use-state
2021-04-29 17:23:03
const[count,setcount]=useState(0)
function inc() {
setcount(count + 5);
setcount(count + 5);
setcount(count + 5);
setcount(count + 5);
}
return (
<div>
  <h1>{count}</h1> #output =5
  <button onClick={() => inc()}>clickme</button>
</div>
);




}
function inc() {
setcount(count=>count + 5);
setcount(count=>count + 5);
setcount(count=>count + 5);
setcount(count=>count + 5);
}
return (
<div>
  <h1>{count}</h1> #output =5
  <button onClick={() => inc()}>clickme</button>
</div>
);
}

当我调用按钮单击计数值的第一个函数时,它变为 5,而在第二个函数中它变为 20。我不知道为什么会出现这种行为

2个回答

仅仅因为这些变量是从 React 钩子返回的,不会使它们的行为与任何其他 JS 变量不同。Aconst是常数;它无法改变。考虑以下没有 React 的示例。

function logValue(input) {
  console.log(input);
}

const value = 0;

logValue(value + 5);
logValue(value + 5);
logValue(value + 5);

每次记录 5。这是你所期望的吗?因为value是常数并且将始终为 0。您可能会认为这不一样,因为您不是在记录,而是在更新。但这真的不一样。状态更新器函数不仅仅是更新一个变量count = 5(提示:它会中断,见下文)。

相反,它会跟踪下一次 render 上的值应该是什么,并且当再次调用函数组件时,有状态的 const 会被分配新的值。

因此,即使您多次调用 updater 函数,您发送给 update 的值始终为5。

关于函数式更新(setCount(count => count + 5)),这里count指的不是状态常量,而是参数count(命名让人混淆,因为状态变量被参数遮蔽了)。此参数保证包含最新的状态值(以帮助解决异步限制)。这就是第二组按预期工作的原因。

这是一个更完整的示例,其中包含显示实际调用事物的时间及其在整个过程中的值的日志:

const {useState, useEffect} = React;

const Example = () => {
  const [count, setCount] = useState(0);
  
  const regularUpdate = () => {
    console.log('count before update:', count);
    setCount(count + 5);
    console.log('count after 1 update:', count);
    setCount(count + 5);
    console.log('count after 2 updates:', count);
    setCount(count + 5);
    console.log('count after 3 updates:', count);
  }
  
  const functionalUpdate = () => {
    console.log('count before update:', count);
    setCount(count => {
      console.log('count inside 1st update:', count);
      return count + 5
    });
    setCount(count => {
      console.log('count inside 2nd update:', count);
      return count + 5
    });
    setCount(count => {
      console.log('count inside 3rd update:', count);
      return count + 5
    });
    console.log('count after updates:', count);
  }
  
  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={regularUpdate}>Run normal updates</button>
      <button onClick={functionalUpdate}>Run functional updates</button>
      <button onClick={() => {setCount(0); console.clear();}}>Reset</button>
    </div>
  );
}

ReactDOM.render(<Example />, document.getElementById('root'));
<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>
<div id="root"></div>

值得注意的是运行功能更新时日志的顺序。请注意,“更新后计数”在功能更新内部的任何日志之前记录,并且它仍然保留未更新的值。

setcount是异步方法,因此setcount将与count第一个inc()方法的初始值(0)同时执行4所以结果将是 5 甚至 4setcount秒被执行。

对于第二种inc()方法,它使用的函数将接收前一个值,并返回一个更新的值,因此结果是预期的 20。