在React组件中使用setTimeouts
setTimeout方法在一定时间后执行一个函数。这对于创建基于时间的效果非常有用,例如在加载新数据之前显示加载旋转图标几秒钟,或者在几秒钟后隐藏成功消息。然而,在React中使用传统的setTimeout可能会具有挑战性。为了保持React应用的效率,我们需要使用useEffect钩子来跟踪和清除这些超时。
让我们了解如何在React组件中处理setTimeouts:
在React组件中使用setTimeouts:
我们可以像在JavaScript中处理一样,在React组件中使用setTimeout方法。在React组件中,setTimeout方法遵循与JavaScript相同的原则。然而,我们应该注意一些注意事项。
要设置超时,我们需要在我们的React组件中使用useEffect钩子。直接在组件内部设置超时可能是一种不恰当的方法,因为每次组件重新渲染时,React都会重新生成setTimeout方法,导致新的超时。我们的应用程序将因为这些超时而迅速变得臃肿并且不可靠。由于多个状态更改,需要进行其他重新渲染。因此,我们将使用useEffect钩子为React组件创建timeout。当组件重新渲染时,useEffect钩子执行副作用。它接受一组依赖项数组以及用于处理副作用的回调函数。这些依赖项是状态变量,当它们变化时,引起这些副作用。我们将把timeout包含在useEffect钩子中以进行管理。
让我们了解在React中实现setTimeout方法的各种方法。
创建React应用:
步骤1: 创建一个项目目录,打开终端,使用以下命令创建一个名为 spinner-gfg 的React应用:
npx create-react-app spinnner-gfg
步骤2: 在创建 spinner-gfg 应用程序之后,通过输入以下命令切换到新文件夹 spinner-gfg:
cd spinner-gfg
项目结构: 现在,我们将修改文件夹并保留此示例所需的文件。现在,请确保您的文件结构如下:
示例1: 我们将使用setTimeout方法,在渲染一些数据之前展示一个加载动画5秒钟。
方法:创建加载动画应用程序:
- 我们将使用useEffect钩子和一个空的依赖数组,在组件挂载后创建一个定时器。
- data 状态变量存储内容, setData 函数更新内容的值。
- 当定时器结束后,数据将会显示, isLoading 状态将被设为false。
index.html: 将以下代码写入你的index.html文件,该文件位于项目目录的public文件夹中:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1" />
<meta name="theme-color"
content="#000000" />
<meta name="description"
content="Web site created using create-react-app" />
<title>Timeouts in React</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
App.js:
import { useState, useEffect } from 'react';
import './App.css'
const App = () => {
const [isLoading, setIsLoading] = useState(true);
const [data, setData] = useState(null);
useEffect(() => {
// Creating a timeout within the useEffect hook
setTimeout(() => {
setData("Welcome to gfg!");
setIsLoading(false);
}, 5000);
}, []);
if (isLoading) {
return <div className='spinner'>
Loading.....</div>;
}
return (
<div className='container'>
{data}
</div>
);
}
export default App;
App.css: 将以下代码添加到 App.css 中以为旋转加载应用程序添加样式。
.spinner,
.container {
margin: 1rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 2rem;
}
.container {
border: 2px solid darkGreen;
}
index.js: 在 index.js 文件中添加以下代码。
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
运行应用程序的步骤: 使用下面的命令运行应用程序:
npm start
输出: 默认情况下,React项目将在端口3000上运行。您可以在浏览器中通过localhost:3000访问它。
解释: useEffect钩子函数调用一个空数组依赖,这意味着它只会在组件挂载时运行一次,并确保定时器只设置一次。isLoading状态变量控制组件的渲染。如果isLoading为true,组件将渲染一个加载旋转器。如果isLoading为false,组件将显示数据。我们可以使用setTimeout方法在useEffect钩子函数中加载数据时显示加载旋转器。
让我们了解如何在重新渲染时创建超时。
清除setTimeout:
在useEffect钩子函数中,我们可以在组件的状态改变时创建超时。然而,每次状态改变时都会生成新的超时,这可能会导致性能问题。必须清除不再需要的超时以防止潜在的内存泄漏。setTimeout方法创建一个定时器,并排队一个回调函数,在指定的时间过去后执行。如果定时器到期并且执行回调函数,超时完成。然而,如果使用相同的变量设置新的超时,上一个超时将被取消,新的超时将被启动。如果超时不在不再需要时被取消,即使设置超时的组件已经卸载,回调函数仍然可能被执行。这可能会导致资源浪费和潜在的错误,特别是如果回调函数具有不再相关的副作用。
为了避免这种情况,我们可以在创建新的超时之前清除上一个超时,使用clearTimeout方法。这对于预防我们的应用中的内存泄漏很重要,因为在组件卸载时,如果不清除setTimeout的回调函数仍然可能执行。
clearTimeout方法:
- clearTimeout取消setTimeout方法生成的超时。
- setTimeout方法返回一个timeoutId,该timeoutId传递给clearTimeout。
- timeoutId标识setTimeout函数生成的特定定时器。
语法:
clearTimeout(timeoutID)
这是一个示例,展示了在重新渲染时如何在React中清除setTimeouts并创建定时器:
示例2: 显示在指定时间后消失的警告消息。
步骤:创建警告消息组件: 我们将显示一个警告消息5秒钟,然后使用setTimeout方法隐藏它。
- 我们将使用 useEffect 钩子在 showWarning 状态为true时设置计时器,并在组件卸载或showWarning状态改变时取消计时器。
- useEffect钩子在组件渲染时调用,意味着每次组件挂载且showWarning状态为true时都会设置一个计时器。
- 为了在组件卸载或showWarning状态改变时取消计时器,我们需要一种存储 计时器ID 并从useEffect钩子的清理函数中访问它的方法。这就是 useRef 钩子的用武之地。
- useRef钩子允许我们创建一个可变的 ref ,在重新渲染时保持不变,可以用于存储计时器ID。当组件挂载时,计时器ID存储在timerId ref的 current 属性中。
- 当组件卸载或showWarning状态改变时,useEffect钩子的清理函数被调用,并使用存储在timerId ref 中的计时器ID取消计时器。
App.js:
import { useState, useEffect, useRef } from 'react';
import './App.css'
const App = () => {
const [showWarning, setShowWarning] = useState(false);
const timerId = useRef(null);
useEffect(() => {
if (showWarning) {
//Creating a timeout
timerId.current = setTimeout(() => {
setShowWarning(false);
}, 5000);
}
return () => {
//Clearing a timeout
clearTimeout(timerId.current);
};
}, [showWarning]);
function handleClick() {
setShowWarning(true);
}
return (
<div className='warn'>
{showWarning && <div className='warningMsg'>
This is a Warning ⚠️!</div>}
<button className='btn' onClick={handleClick}>
Show Warning</button>
</div>
);
}
export default App;
App.css: 在 App.css 中添加以下代码以给消息应用程序添加样式。
.warn {
margin: 1rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 1rem;
}
.warningMsg {
border: 2px solid orangered;
background-color: rgb(210, 153, 124);
}
.btn {
border: 2px solid darkGreen;
border-radius: 10px;
margin: 1rem;
cursor: pointer;
}
运行应用程序的步骤:
使用以下命令运行应用程序:
npm start
输出:
解释:
组件挂载时,使用一个空的showWarning数组调用useEffect钩子,这意味着效果只会运行一次。if (showWarning)条件为false,所以计时器不会被设置。当用户点击Show Warning按钮时,会调用handleClick函数,该函数将showWarning状态设置为true。这会导致组件重新渲染并再次调用useEffect钩子。这次,if (showWarning)条件为true,所以使用setTimeout设置计时器,在5秒后隐藏警告消息。
如果用户在5秒内再次点击Show Warning按钮,则showWarning状态将再次被设置为true,导致组件重新渲染并再次调用useEffect钩子。这将取消上一个计时器,并设置一个新计时器,在5秒后隐藏警告消息。
如果用户在5秒内离开页面,则组件将被卸载,并调用useEffect钩子的清理函数。清理函数使用保存在timerId引用中的计时器ID来调用clearTimeout,取消计时器并防止回调函数被执行。
这样我们就可以在5秒后自动隐藏警告消息,而不会产生不必要的副作用。