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在找到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之后的代码随后运行。那么,为什么我们在上述情况下会有不同的行为呢?解释是什么?引擎盖下发生了什么?

-第一个问题

根据Dan Abramov(联合创建者:Redux,创建React应用程序(

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

无论您在React事件处理程序中执行多少组件中的setState((调用,它们都将在事件结束时只生成一个重新呈现

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

-第二个问题

根据Dan Abramov

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

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

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

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

这是预期的行为,react通常只有在状态或道具发生变化时才会重新渲染组件。React的useState是一个异步函数,这就是您的console.log订单未匹配的原因。

您可以使用useEffect来监听更改并根据需要启动函数。

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>
</>
);
};
const setBoth = () => {
setRunning(true);
setJumping(true);
};

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

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

//";1〃;

//";2〃;

//";3〃=======>在再次渲染之后。

UseEffect是一个在重新渲染后调用的react钩子。

因此,首先;嘿"打印=====>成分已经渲染

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

因此,它打印";你好;。

最新更新