当前位置: 首页 > 知识库问答 >
问题:

useState反应挂钩的奇怪行为

穆展鹏
2023-03-14

我在useStateReact hook中遇到了一些非常奇怪的行为。在下面的代码中(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);

单击按钮时,我们在控制台中获得以下序列:

one
two
zero

我希望:

zero
one
zero
two

因为我认为如果React找到useStatesetter并在重新渲染后执行以下代码,它会立即重新渲染。此外,我的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.details值与状态变量time的内容不同时,上述代码会发生什么情况:

Hey
Hello

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

共有3个答案

漆雕亮
2023-03-14
 const setBoth = () => {

    setRunning(true);
    setJumping(true);

 };

上述代码称为批处理。这意味着通过同时调用setRunning和setJumping,react、幕后更新。这将导致一次重新渲染。

因此,当您单击更改两种状态的按钮时,两种状态将同时设置。因此我们印刷

// "1"

//"2"

//"3" =======

UseEffect是在重新渲染后调用的react挂钩。

因此,首先打印“嘿”=====

接下来,将调用useEffect,因为重新渲染后react将调用useEffect。

因此它会打印“你好”。

孟杰
2023-03-14

这是预期的行为,通常仅当状态或道具更改时才重新渲染组件。React的useState是一个异步函数,这就是console.log订单不匹配的原因。

您可以使用useffect收听更改并根据需要启动函数。

Codesandbox链接


const Comp = ({ flag }) => {
  const [running, setRunning] = useState(false);
  const [jumping, setJumping] = useState(false);
  const setZero = () => {
    console.log("zero");
  };

  useEffect(() => {
    setJumping(!running);
    console.log("two");
  }, [running]);

  const setBoth = () => {
    setZero();
    setRunning(!running);
    console.log("one");
  };

  return (
    <>
      <pre>{JSON.stringify({ running, jumping })}</pre>
      <button onClick={() => setBoth()}>setboth</button>
    </>
  );
};
楚和悌
2023-03-14

-第一个问题

根据丹·阿布拉莫夫(联合创始人:Redux,创建React应用程序)的说法

目前(React 16及更早版本),默认情况下只有React事件处理程序中的更新是批处理的。有一个不稳定的API,当您需要时,可以在事件处理程序之外强制批处理。

不管你在React事件处理程序中调用了多少setState()组件,它们在事件结束时只会产生一个重新渲染

在第一种情况下,click事件是一个react事件

-第二个问题

根据丹·阿布拉莫夫的说法

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

window.addEventListener不是react事件,因此应立即呈现它。

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

我在这里举了一个包含这两个场景的示例

 类似资料:
  • 问题内容: 在带有钩子的React中,更新状态的正确方法是嵌套对象是什么? 一个人怎么会使用到的更新来(附加一个字段)? (改变价值)? 问题答案: 您可以像这样传递新值

  • 我有一个表单组件,它从其父级获取其状态。表单组件只呈现一些输入字段和其他字段。 父组件使用useReucer并将值向下传递给表单组件。 有两个父组件,一个允许用户使用表单创建,另一个允许他们编辑。 在编辑父组件中,我使用useEffect从api获取数据,并从服务器设置初始状态。 在开发构建中,当组件呈现时,有时会出现以下错误: 超过最大更新深度。当组件在componentWillUpdate或c

  • 更新状态的正确方式是什么,是一个嵌套对象,在与钩子反应? 如何使用将更新为(附加字段)? (更改值)?

  • 我正在尝试新的hooks功能,并坚持认为我的状态没有更新。 实际上,状态已更新(我可以在console.log中看到更新的值,并且我的组件会重新运行useEffect),但是useEffect方法使用我的旧状态,并仅将签名保存给第一个用户,而活动用户在状态中确实发生了更改。 我想过添加useCallback,并将我的方法移动到use效果或组件本身,但我可以设法让它工作。 状态: 这是我的效果: 这

  • 因此,我有一些代码并没有像预期的那样工作。当前焦点是在父组件上使用useState()设置的,因此它是一个状态变量。但是,当父级中的currentFocus值更改时,此处的focus变量本身不会更新。我希望重新呈现父组件,这反过来会重新呈现该组件,从而导致foucs值发生更改。 现在我可以通过执行以下操作来解决此问题, 但我只想在更新focus/currentFocus时运行useEffect挂钩