reactjs useState React钩子的奇怪行为

z8dt9xmd  于 2023-10-17  发布在  React
关注(0)|答案(3)|浏览(111)

我在useState React钩子上遇到了一些非常奇怪的行为。在下面的代码(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 setter,下面的代码会在重新渲染后执行。此外,我的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

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

6tdlim6h

6tdlim6h1#

  • 第一个问题
    根据 * 丹·阿布拉莫夫 *(联合创作者:Redux,Create React App.)

目前(React 16及更早版本),默认情况下只有React事件处理程序内部的更新是批量处理的。有一个不稳定的API可以在您需要时在事件处理程序之外强制执行事件处理程序,以应对罕见的情况。
无论你在React事件处理程序中执行了多少次setState()调用,它们都只会在事件结束时产生一个re-render

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

  • 第二个问题
    作者:Dan Abramov

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

window. addEventEvent不是一个react事件,所以它应该立即呈现。
你可以在这里找到 Dan Abramov 的完整答案
我制作了一个包含两个场景的here示例

mrphzbgm

mrphzbgm2#

这是预期的行为,react通常只在状态或 prop 改变时才重新渲染组件。React的useState是一个异步函数,这就是console.log命令不匹配的原因。
您可以使用useEffect来监听更改并根据需要触发函数。
Codesandbox link

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>
    </>
  );
};
9w11ddsr

9w11ddsr3#

const setBoth = () => {

    setRunning(true);
    setJumping(true);

 };

上面的代码被称为“”。这意味着react在后台通过同时调用setRunning和setJumping来更新。这将导致单个重新渲染。
因此,当您单击更改两种状态的按钮时,两种状态都会立即设置。因此,我们打印
//“1”
//“2”
//“3”=>再次渲染后。
React Effect是一个react钩子,在重新渲染后调用。
因此,首先打印“嘿”=>组件已渲染
接下来,useEffect被调用,因为useEffect在重新渲染后被react调用。
因此,它打印“Hello”。

相关问题