我正在尝试模拟在网络应用程序上运行的脚本。这个想法是脚本在后台运行并启动 http 请求,然后这些请求应显示为 <p>
。在一个分区中。但我不想让它走得太快。首先,状态更新太快不能正确更新,其次用户很难看到。
我试过使用 setTimeout,但这不起作用,因为它似乎设置了超时,然后继续 setState 而不等待。
AddParagraph = () => {
for(var i = 0; i < 20; i++){
setTimeout(function(){
this.setState({
urls: [...this.state.urls, www.url.com/i]
})},2000)
}
}
我读到在 for 循环中设置状态不是一个好主意,因为它没有时间快速渲染/更新。但是我看不出这样做的好主意,我不应该为此使用状态吗?
最佳答案
您似乎想要添加请求的 URL 并将其记录在网页上。
因此,您似乎对使用 for
更新状态“强制”感到困惑循环。
React 在本质上更像是一种声明,您的组件通常会对“状态”变化使用react。
所以不用 setTimeout
,这需要使用 for
循环,让我们以设定的时间间隔对状态变化做出“ react ”。
类组件 (CC) 版本
如我所见this.state
,我假设您使用的是 CC。
class ClassRequestViewer extends Component {
state = {
urls: [],
postId: 1
};
// To be used to clear the interval
// 👇 to prevent memory leaks
intervalId = undefined;
// Add a paragraph while setting the new post ID
// 👇 together in one shot.
addParagraph = () => {
const newUrl = `${urlRoot}/${this.state.postId}`;
this.setState(prevState => ({
urls: prevState.urls.concat(newUrl),
postId: prevState.postId + 1
}));
};
// 👇 You would want to initialize the interval only ONCE
// as the changing the state in `addParagraph` will
// cause `ClassRequestViewer` to re-render (in `render()` below).
componentDidMount() {
this.intervalId = setInterval(this.addParagraph, timeout);
}
// ⚠👇 is important!
// We need to make sure to clean up to prevent memory leak.
// OR else, the interval will run even if the component is unmounted
// and unavailable.
componentWillUnmount() {
this.intervalId && clearInterval(this.intervalId);
}
// 👇 Whenever "addParagraph" updates the state,
// This gets called, so will show the requests sent automatically.
render() {
const { urls } = this.state;
return (
<ul style={{ listStyle: "none" }}>
{urls.map(url => (
<li key={url}>Request sent to {url}</li>
))}
</ul>
);
}
}
现在您可以看到以给定时间间隔发送以呈现的请求列表。
函数组件 (FC) 版本
我还在下面的 FC 中使用 Hook (useState
、useEffect
)实现了相同的 CC 版本。
代码看起来更小为 useEffect
有效地将关注点(setInterval
和 clearInterval
)“共同定位”在一个地方。
function RequestViewer() {
const [urls, setUrls] = useState([]);
const [postId, setPostId] = useState(1);
useEffect(() => {
function addParagraph() {
const newUrl = `${urlRoot}/${postId}`;
setUrls(_ => _.concat(newUrl));
setPostId(postId + 1);
}
const intervalId = setInterval(addParagraph, timeout);
return () => clearInterval(intervalId);
}, [postId]);
return (
<ul style={{ listStyle: "none" }}>
{urls.map(url => (
<li key={url}>Request sent to {url}</li>
))}
</ul>
);
}
没有for
在那里循环,因为useEffect
每当 postId
时更新改变了,这是每个 timeout
秒 setInterval
.
I want it to be started on an onClick event and stopped after X amount of rows. This seems to run endlessly?
正如您在 comment 中提到的那样, 它连续运行。
为了能够“开始/结束”该过程,您需要将“查看器”保留为查看器并处理父级上的显示。
因此,当您“启动”该过程时,查看器 ClassRequestViewer
将启动请求并向您显示结果,然后单击“停止”将卸载组件(这就是我使用 componentWillUnmount
到 clearInterval
的原因)。
我已经更新了 App
有 start/stop
按钮。
function App() {
const [isStarted, setIsStarted] = useState(false);
return (
<div className="App">
<h1>Request Viewer</h1>
{!isStarted && (
<button onClick={() => setIsStarted(true)}>Start the request</button>
)}
{isStarted && (
<>
<button onClick={() => setIsStarted(false)}>Stop Requests</button>
<ClassRequestViewer />
</>
)}
</div>
);
}
当您点击 Start the request
时按钮,它设置isStarted
至 true
,因此 ClassRequestViewer
已加载,您可以看到请求。
下面的代码已加载
{isStarted && (
<>
<button onClick={() => setIsStarted(false)}>Stop Requests</button>
<ClassRequestViewer />
</>
)}
当您点击 Stop Request
时按钮,设置isStarted
至 false
,因此,ClassRequestViewer
卸载,(依次调用 componentWillUnmount -> clearInterval
进行清理)。
然后你会看到 Start the request
再次按钮。
{!isStarted && (
<button onClick={() => setIsStarted(true)}>Start the request</button>
)}
下面是更新后的 App
的工作演示.
关于javascript - 在几秒钟内逐渐更新状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57364530/