我正在尝试使用useState
钩子以编程方式更改状态。
我是这样定义的:
const [data, setData] = React.useState({
text: "Hello StackOverflow!",
someNumber: 2
});
出于测试目的,我创建了一个间隔,该间隔每秒钟增加someNumber
。
setInterval(() => {
setData({ ...data, someNumber: data.someNumber + 1 });
}, 1000);
当我现在安装组件时,文本开始以不同的数字快速闪烁。我想间隔启动了多次,但我不知道为什么。这是我第一个使用新钩子的项目。
可以找到我的代码的完整示例on code sandbox snippet
如果链接断开,则为以下组件:
import React from "react";
import TextField from "@material-ui/core/TextField";
export default function StateTextFields() {
const [data, setData] = React.useState({
text: "Hello StackOverflow!",
someNumber: 2
});
setTimeout(() => {
setData({ ...data, text: "How are you?" });
}, 1000);
setInterval(() => {
setData({ ...data, someNumber: data.someNumber + 1 });
}, 1000);
return (
<TextField
id="standard-name"
label="Name"
value={data.text + " " + data.someNumber}
margin="normal"
/>
);
}
答案 0 :(得分:2)
将setInterval
移到useEffect
内。这将阻止它为每个渲染创建新的间隔。
React.useEffect(() => {
setInterval(() => {
setData(prevState => ({ ...prevState, someNumber: prevState.someNumber + 1 }));
}, 1000);
setTimeout(...)
},[])
这将使这些功能仅在组件安装时运行一次。
无条件调用一个函数来修改功能组件内部的状态,就像在类组件的render
中进行操作一样。它将修改状态,从而导致重新渲染,这将无限地修改状态,等等。
您需要使用prevState
回调进行更新。这是因为useEffect
没有像data
那样使useEffect(() => {}, [data])
过时,而没有听对data
的更改。但是我们也不想更改它以听取更改,因为那样会使您的间隔再次无限创建。因此,我们使用prevState
,它始终使用状态的最新副本。
答案 1 :(得分:0)
您要在每个组件渲染调用上设置setTimeout
和setInterval
。
答案 2 :(得分:0)
您的超时/间隔将在每次重新渲染时触发。您必须将其放入useEffect
中,以替换生命周期挂钩,例如componentDidMount()
。
示例:
useEffect(()=>{
const myInterval = setInterval(() => {
setData({ ...data, someNumber: data.someNumber + 1 })
}, 1000);
return ()=>{myInterval.clearInterval()} //cleanup
}, []) //The empty array means "only do this once, after the first render"
答案 3 :(得分:0)
这里有多个问题。
timeout
与data
几乎同时更改interval
。导致超时计算2 + 1
,同时间隔认为data.someNumber
仍然是2
而不是3
。React.useEffect
这样的钩子时,react每次重新释放组件时都将重新创建方法,即每次状态更改时也将重新创建方法,每次data
更改时也将重新创建方法。BrianThompson指出以下解决方案可以解决这些问题。我添加了一些评论来解释原因。
React.useEffect(() => {
setInterval(() => {
// using prevState is essential here because it negates the need for 'data' as
// that would force you to recreate the hook every time 'data' changes
setData(prevState => ({ ...prevState, someNumber: prevState.someNumber + 1 }));
}, 1000);
setTimeout(...)
},[])