javascript - 每秒更新 useEffect 中的状态 10 秒
问题描述
通过不进入实际使用,我创建了一个简单的示例来解释我想要做什么。
我有一个状态对象{num:0}
,我想在每秒 10 秒后更新 num,据此,我创建了一个运行良好的类组件。
class App extends React.Component {
constructor() {
super();
this.state = {
num: 0
};
}
componentDidMount = () => {
for (let i = 0; i < 10; i++) {
setTimeout(() => this.setState({ num: this.state.num + 1 }), i * 1000);
}
};
render() {
return (
<>
<p>hello</p>
<p>{this.state.num}</p>
</>
);
}
}
现在我想在功能组件中复制相同的功能,但我做不到。我尝试如下所示:
const App = () => {
const [state, setState] = React.useState({ num: 0 });
React.useEffect(() => {
for (let i = 0; i < 10; i++) {
setTimeout(() => setState({ num: state.num + 1 }), i * 1000);
}
}, []);
return (
<>
<p>hello</p>
<p>{state.num}</p>
</>
);
};
谁能帮我解决我在这里做错的事情?
解决方案
你所有的超时都会运行,但是因为你在第一次渲染时设置了它们,所以你在初始state.num
值周围创建了一个闭包,所以当每个超时都会将新的状态值设置为0 + 1
并且没有任何变化。
注释中提到的副本涵盖了详细信息,但这里有一个快速工作片段,它使用 aref
作为计数器在 10 次迭代后停止并在 useEffect 返回时清理计时器。
const App = () => {
const [state, setState] = React.useState({ num: 0 });
const counter = React.useRef(0);
React.useEffect(() => {
if (counter.current < 10) {
counter.current += 1;
const timer = setTimeout(() => setState({ num: state.num + 1 }), 1000);
return () => clearTimeout(timer);
}
}, [state]);
return (
<div>
<p>hello</p>
<p>{state.num}</p>
</div>
);
};
ReactDOM.render(
<App />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
为了使您的代码按原样工作,一次设置所有这些,您可以将回调传递给setState()
调用以避免创建闭包,但是您放弃了通过在每个渲染上设置新超时所允许的精细控制。
const App = () => {
const [state, setState] = React.useState({ num: 0 });
React.useEffect(() => {
for (let i = 0; i < 10; i++) {
setTimeout(() => setState(prevState => ({ ...prevState, num: prevState.num + 1 })), i * 1000);
}
}, []);
return (
<div>
<p>hello</p>
<p>{state.num}</p>
</div>
);
};
ReactDOM.render(
<App />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
推荐阅读
- api - 从 golang 中持久化卷的 OpenShift API 获取和修改 yaml 文件
- mysql - 基于另一列约束一列
- javascript - 一旦可用,如何选择对象值?
- javascript - 如何在网上商店销售价格上设置价格颜色
- mapbox - 如何根据 bbox 坐标计算位置的经度/纬度
- python - wxPython - 在除第一列之外的任何其他列中将图像添加到 ListCtrl?
- cuda - CUDA 上 PGI Fortran 编译器中的常量内存
- mysql - 1265, "第 1218 行的列 'name' 的数据被截断
- mysql - 操作数应在我的 sql 查询中包含 1 列错误
- html - 语义 HTML 和 BEM CSS