首页 > 解决方案 > useState React 钩子的奇怪行为

问题描述

我在使用useStateReact 钩子时遇到了一些非常奇怪的行为。在以下代码中(https://codesandbox.io/s/purple-bush-nb5uy?file=/src/index.js):

function App() {
  return (
    <div className="App">
      <Comp flag={true} />
    </div>
  );
}
const Comp = ({ flag }) => {
  const [running, setRunning] = useState(false);
  const [jumping, setJumping] = useState(false);
  console.log('zero');
  
  const setBoth = () => {
    setRunning(true);
    console.log('one');
    setJumping(true);
    console.log('two');
  };

  return (
    <>
      {"running: " + running}
      {"jumping: " + jumping}
      <button onClick={() => setBoth()}>setboth</button>
    </>
  );
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

当我们单击 时button,我们会在控制台中输入以下序列:

one
two
zero

我希望:

zero
one
zero
two

因为我认为如果 React 找到一个useState设置器,它会立即重新渲染,并且在重新渲染后执行以下代码。此外,我的 React 应用程序就是这种情况:

const [time, setTime] = useState('');
console.log('Hey');
const updateTime = (e) => {
        setTime(e.details);
        console.log('Hello');
    };

    useEffect(() => {
        window.addEventListener("updateTime", updateTime);            
        return () => {
            window.removeEventListener("updateTime", updateTime);
        }
    }, []);

上面的代码在updateTime运行时以及当e.detailsvalue 与 state variabletime的内容不同时会发生什么:

Hey
Hello

换句话说,首先运行重新渲染,然后运行 ​​setter 之后的代码。那么,为什么我们在上述情况下会有不同的行为呢?解释是什么?幕后发生了什么?

标签: reactjsreact-hooks

解决方案


-第一个问题

根据Dan Abramov(共同创建者:Redux,创建 React 应用程序。)

目前(React 16 和更早版本),默认情况下,仅对 React 事件处理程序内的更新进行批处理。有一个不稳定的 API 可以在您需要的极少数情况下强制在事件处理程序之外进行批处理。

无论您在 React 事件处理程序中执行多少个组件中的多少个 setState() 调用,它们都只会在事件结束时产生一次重新渲染

在你的第一种情况下,点击事件是一个反应事件

-第二个问题

根据丹·阿布拉莫夫

但是,在 React 16 和更早的版本中,默认情况下在 React 事件处理程序之外还没有批处理。因此,如果在您的示例中我们有一个 AJAX 响应处理程序而不是 handleClick,则每个 setState() 都会在发生时立即处理。在这种情况下,是的,您会看到一个中间状态:

window.addEventListener 不是反应事件,所以应该立即渲染。

你可以在 这里找到Dan Abramov的完整答案

我在这里做了一个包含两个场景的例子


推荐阅读