我们已迁移到“反应功能组件”,而不是“基于类的组件”。我找不到setState
callback function的替代逻辑。即,我有一个带有状态的功能组件,并且我想创建一个事件处理程序函数,该函数按顺序多次更改状态,但需要注意的是,我不知道状态的当前值(可能是true
/ false
)。以下示例可能更有意义。
const Example = () => {
const [ openDoor, setOpenDoor ] = useState(false);
// the following handler should swich 'openDoor' state to inverse of
// current state value. Then after setTimeout duration, inverse it again
const toggleOpenDoor = () => {
setOpenDoor(!openDoor);
// within setTimeout below, '!openDoor' does not work because it still
// receives the same value as above because of async nature of
// state updates
setTimeout(() => setOpenDoor(!openDoor), 500)
}
return(...);
}
在基于class
的组件中,我们具有回调参数,该参数将在上一次更新后更新状态。如何使用状态挂钩在上述功能组件中实现相同的目的?
答案 0 :(得分:2)
您可以使用useEffect挂钩查看状态更改的发生时间。
useEffect(() => {
// do something
console.log('openDoor change', openDoor)
}, [openDoor]);
答案 1 :(得分:2)
我想知道useEffect是否是最佳解决方案。特别是在useEffect中调用setTimeout会导致无限循环,因为每次我们调用setOpenDoor时,应用都会呈现,然后调用useEffect再次调用setTimeOut,该方法将调用setOpenDoor函数...
setTimeout -> setOpenDoor -> useEffect -> setTimeout -> ... hell
当然,您可以使用if语句wihin useEffect,其使用方式与@ksav建议的方式相同,但不能满足@Kayote的一项要求:
我不知道state的当前值(可能为true / false)
这是一种无需useEffect即可完成上述要求的解决方案:
在那里,看到这段代码的重要性:
const toggleOpenDoor = () => {
setOpenDoor(!openDoor);
setTimeout(() => setOpenDoor(openDoor => !openDoor), 500);
};
由于我们正在使用setTimeout,因此需要将回调传递给setOpenDoor而不是更新状态。这是因为我们要发送“当前”状态。如果改为发送新状态,则在setTimeOut处理该状态时,它将已经更改(因为在setTimeOut使用setOpenDoor(!openDoor);执行其回调之前,我们已进行了更改),并且不会进行任何更改。
答案 2 :(得分:1)
我将告诉您,它的工作方式与this.setState
几乎相同,只是传递了一个回调函数,该函数以以前的状态为参数并返回新状态(docs)< / p>
const Example = () => {
const [openDoor, setOpenDoor] = useState(false);
const toggleOpenDoor = () => {
setOpenDoor(!openDoor);
setTimeout(() => setOpenDoor(prevDoor => !prevDoor), 500)
}
return(...);
}
为了让您知道它何时发生更改,您可以使用useEffect
回调,每次依赖项数组(docs)发生变化时都会调用该回调
const Example = () => {
const [openDoor, setOpenDoor] = useState(false);
useEffect(() => {
console.log('openDoor changed!', openDoor)
}, [openDoor])
const toggleOpenDoor = () => {
setOpenDoor(!openDoor);
setTimeout(() => setOpenDoor(prevDoor => !prevDoor), 500)
}
return(...);
}
:)
答案 3 :(得分:1)
您可以使用useEffect
钩子来实现此目的。
setOpenDoor(!openDoor);
useEffect(() => {
// Here your next setState function
}, [openDoor]);
有关钩子的更多信息,请查看https://reactjs.org/docs/hooks-effect.html
答案 4 :(得分:1)
您应该只在setTimeout
回调中使用useEffect
:
const App = () => {
const [openDoor, setOpenDoor] = useState(false);
const toggle = () => setOpenDoor(prevOpen => !prevOpen);
useEffect(() => {
const id = setTimeout(() => toggle(), 1000);
return () => clearTimeout(id);
}, [openDoor]);
return <Container>isOpen: {String(openDoor)}</Container>;
};
答案 5 :(得分:1)
import React, { useState, useEffect } from "react";
const Example = () => {
const [openDoor, setOpenDoor] = useState(false);
const toggleOpenDoor = () => {
setOpenDoor(!openDoor);
};
useEffect(() => {
console.log(openDoor);
if (openDoor) {
setTimeout(() => setOpenDoor(!openDoor), 1500);
}
}, [openDoor]);
return (
<>
<button onClick={toggleOpenDoor}>Toggle</button>
<p>{`openDoor: ${openDoor}`}</p>
</>
);
};
export default Example;