在几秒钟内逐渐更新状态

我正在尝试模拟在 Web 应用程序上运行的脚本。这个想法是一个脚本在后台运行并启动 http 请求,然后这些请求应该显示为<p>一个 div。但我不希望它走得太快。一是状态更新太快,更新不正确,二是让用户难以看到。


我曾尝试使用 setTimeout,但这不起作用,因为它似乎正在设置超时,然后继续 setState 而无需等待。


AddParagraph = () => {


for(var i = 0; i < 20; i++){

 setTimeout(function(){

 this.setState({

    urls: [...this.state.urls, www.url.com/i]

 })},2000)

 }

}

我已经读过在 for 循环中设置状态不是一个好主意,因为它没有时间那么快地呈现/更新。但是我认为这样做不是一个好主意,我不应该为此使用状态吗?


侃侃无极
浏览 155回答 2
2回答

叮当猫咪

您似乎想添加请求的 URL 并将其记录在网页上。因此,您似乎对使用for循环“强制”更新状态感到困惑。React 本质上更像是一种声明性的,您的组件通常会对“状态”变化做出反应。因此setTimeout,与其使用需要使用for循环的 using ,不如让我们在设定的时间间隔内对状态变化做出“反应”。类组件 (CC) 版本如我所见this.state,我假设您使用的是 CC。class ClassRequestViewer extends Component {&nbsp; state = {&nbsp; &nbsp; urls: [],&nbsp; &nbsp; postId: 1&nbsp; };&nbsp; // To be used to clear the interval&nbsp; // 👇 to prevent memory leaks&nbsp; intervalId = undefined;&nbsp; // Add a paragraph while setting the new post ID&nbsp; // 👇 together in one shot.&nbsp; addParagraph = () => {&nbsp; &nbsp; const newUrl = `${urlRoot}/${this.state.postId}`;&nbsp; &nbsp; this.setState(prevState => ({&nbsp; &nbsp; &nbsp; urls: prevState.urls.concat(newUrl),&nbsp; &nbsp; &nbsp; postId: prevState.postId + 1&nbsp; &nbsp; }));&nbsp; };&nbsp; // 👇 You would want to initialize the interval only ONCE&nbsp; // as the changing the state in `addParagraph` will&nbsp; // cause `ClassRequestViewer` to re-render (in `render()` below).&nbsp; componentDidMount() {&nbsp; &nbsp; this.intervalId = setInterval(this.addParagraph, timeout);&nbsp; }&nbsp; // ⚠👇 is important!&nbsp; // We need to make sure to clean up to prevent memory leak.&nbsp; // OR else, the interval will run even if the component is unmounted&nbsp; //&nbsp; and unavailable.&nbsp; componentWillUnmount() {&nbsp; &nbsp; this.intervalId && clearInterval(this.intervalId);&nbsp; }&nbsp; // 👇 Whenever "addParagraph" updates the state,&nbsp; // This gets called, so will show the requests sent automatically.&nbsp; render() {&nbsp; &nbsp; const { urls } = this.state;&nbsp; &nbsp; return (&nbsp; &nbsp; &nbsp; <ul style={{ listStyle: "none" }}>&nbsp; &nbsp; &nbsp; &nbsp; {urls.map(url => (&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <li key={url}>Request sent to {url}</li>&nbsp; &nbsp; &nbsp; &nbsp; ))}&nbsp; &nbsp; &nbsp; </ul>&nbsp; &nbsp; );&nbsp; }}现在您可以看到在给定时间间隔内发送到渲染的请求列表。功能组件 (FC) 版本我还在下面的 FC 中使用钩子 ( useState, useEffect)实现了相同的 CC 版本。代码看起来更小,因为在一个地方useEffect有效地“共同定位”关注点 ( setInterval& clearInterval)。function RequestViewer() {&nbsp; const [urls, setUrls] = useState([]);&nbsp; const [postId, setPostId] = useState(1);&nbsp; useEffect(() => {&nbsp; &nbsp; function addParagraph() {&nbsp; &nbsp; &nbsp; const newUrl = `${urlRoot}/${postId}`;&nbsp; &nbsp; &nbsp; setUrls(_ => _.concat(newUrl));&nbsp; &nbsp; &nbsp; setPostId(postId + 1);&nbsp; &nbsp; }&nbsp; &nbsp; const intervalId = setInterval(addParagraph, timeout);&nbsp; &nbsp; return () => clearInterval(intervalId);&nbsp; }, [postId]);&nbsp; return (&nbsp; &nbsp; <ul style={{ listStyle: "none" }}>&nbsp; &nbsp; &nbsp; {urls.map(url => (&nbsp; &nbsp; &nbsp; &nbsp; <li key={url}>Request sent to {url}</li>&nbsp; &nbsp; &nbsp; ))}&nbsp; &nbsp; </ul>&nbsp; );}没有for循环有两种,因为useEffect被更新时postId被改变,这是每一个timeout在几秒钟setInterval。我希望它在 onClick 事件上启动并在 X 行后停止。这似乎无休止地运行?正如您在评论中提到的,它会持续运行。为了能够“开始/顶部”该过程,您将“查看器”作为查看器并处理父级上的显示。所以,当你的“开始”的过程中,观众ClassRequestViewer将启动该请求,并显示你的结果,并在“STOP”将卸载的组件点击(这就是为什么我使用componentWillUnmount到clearInterval)。我已经更新了App有start/stop按钮。function App() {&nbsp; const [isStarted, setIsStarted] = useState(false);&nbsp; return (&nbsp; &nbsp; <div className="App">&nbsp; &nbsp; &nbsp; <h1>Request Viewer</h1>&nbsp; &nbsp; &nbsp; {!isStarted && (&nbsp; &nbsp; &nbsp; &nbsp; <button onClick={() => setIsStarted(true)}>Start the request</button>&nbsp; &nbsp; &nbsp; )}&nbsp; &nbsp; &nbsp; {isStarted && (&nbsp; &nbsp; &nbsp; &nbsp; <>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <button onClick={() => setIsStarted(false)}>Stop Requests</button>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <ClassRequestViewer />&nbsp; &nbsp; &nbsp; &nbsp; </>&nbsp; &nbsp; &nbsp; )}&nbsp; &nbsp; </div>&nbsp; );}当您单击Start the request按钮时,它设置isStarted为true,从而ClassRequestViewer加载,您可以看到请求。下面的代码已加载{isStarted && (&nbsp; <>&nbsp; &nbsp; <button onClick={() => setIsStarted(false)}>Stop Requests</button>&nbsp; &nbsp; <ClassRequestViewer />&nbsp; </>)}当您单击Stop Request按钮时,该按钮设置isStarted为false,从而ClassRequestViewer卸载(反过来调用componentWillUnmount -> clearInterval清理)。然后你会Start the request再次看到按钮。{!isStarted && (&nbsp; <button onClick={() => setIsStarted(true)}>Start the request</button>)}下面是更新的App.
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript