ReactJS - 使用 useEffect
React 提供 useEffect 来在组件中执行副作用。一些副作用如下 −
从外部源获取数据并更新渲染的内容。
渲染后更新 DOM 元素。
订阅
使用计时器
日志记录
在基于类的组件中,这些副作用是使用生命周期组件完成的。因此,useEffect Hooks(钩子)是下面提到的生命周期事件的效果替代品。
componentDidMount −首次渲染完成后触发。
componentDidUpdate − 由于 prop 或 state 更改而更新渲染后触发。
componentWillUnmount − 在组件销毁期间卸载渲染内容后触发。
本章让我们学习如何使用 effect hook。
useEffect 的签名
useEffect 的签名如下 −
useEffect( <update function>, <dependency> )
其中,update 函数的签名如下 −
{ // code return <clean up function> }
这里,
Update function − Update function 是每次渲染阶段后执行的函数。这对应于 componentDidMount 和 componentDidUpdate 事件
Dependency − Dependency 是一个包含函数所依赖的所有变量的数组。指定依赖关系对于优化 effect hook 非常重要。一般来说,update function 会在每次渲染后调用。有时没有必要在每次渲染时都渲染 update function。假设我们从外部源获取数据,并在渲染阶段之后对其进行更新,如下所示 −
const [data, setDate] = useState({}) const [toggle, setToggle] = useState(false) const [id, setID] = useState(0) useEffect( () => { fetch('/data/url/', {id: id}).then( fetchedData => setData(fetchedData) ) }) // code
只要更新 data 和 toggle 变量,组件就会重新渲染。但如您所见,我们不需要在每次更新切换状态时运行定义的效果。要解决此问题,我们可以传递一个空依赖项,如下所示 −
const [data, setDate] = useState({}) const [toggle, setToggle] = useState(false) const [id, setID] = useState(0) useEffect( () => { fetch('/data/url/', { id: id }).then( fetchedData => setData(fetchedData) ) }, [])
上述代码在第一次渲染后只会运行一次效果。即使它可以解决问题,每次更改 id 时都必须运行效果。为了实现这一点,我们可以将 id 作为效果的依赖项,如下所示 −
const [data, setDate] = useState({}) const [toggle, setToggle] = useState(false) const [id, setID] = useState(0) useEffect( () => { fetch('/data/url/', { id: id }).then( fetchedData => setData(fetchedData) ) }, [id])
这将确保仅在修改 id 后才会重新运行效果
清理功能 −清理函数用于在使用订阅函数和定时器函数过程中进行清理工作,如下所示 −
const [time, setTime] = useState(new Date()) useEffect(() => { let interval = setInterval(() => { setTime(new Date()) }, 1000) return () => clearInterval(interval) }, [time])
让我们创建一个完整的应用程序来了解后面部分的清理功能。
effect hook 的功能
effect hook 的一些显著功能如下 −
React 允许在一个函数组件中使用多个 effect hook。这将帮助我们为每个副作用编写一个函数并将其设置为单独的效果。
每个 hook 将按照声明的顺序运行。开发人员应确保正确声明效果的顺序。
依赖项功能可用于提高性能和正确执行副作用。
清理功能可防止内存泄漏和不必要的事件触发。
使用效果获取数据
让我们创建一个应用程序,它将从外部源获取数据并使用本节中的 useEffect Hooks(钩子)对其进行渲染。
首先,创建一个新的 React 应用程序并使用以下命令启动它。
create-react-app myapp cd myapp npm start
接下来,在组件文件夹 (src/components/NameList.js) 下创建一个 React 组件 NameList
function NameList() { return <div>names</div> } export default NameList
此处,NameList 组件的目的是展示常见名称的流行列表
接下来,更新根组件 App.js 以使用新创建的 NameList 组件。
import NameList from "./components/NameList"; function App() { return ( <div style={{ padding: "5px"}}> <NameList /> </div> ); } export default App;
接下来,创建一个 json 文件 names.json (public/json/names.json),并以 json 格式存储流行名称,如下所示。
[ { "id": 1, "name": "Liam" }, { "id": 2, "name": "Olivia" }, { "id": 3, "name": "Noah" }, { "id": 4, "name": "Emma" }, { "id": 5, "name": "Oliver" }, { "id": 6, "name": "Charlotte" }, { "id": 7, "name": "Elijah" }, { "id": 8, "name": "Amelia" }, { "id": 9, "name": "James" }, { "id": 10, "name": "Ava" }, { "id": 11, "name": "William" }, { "id": 12, "name": "Sophia" }, { "id": 13, "name": "Benjamin" }, { "id": 14, "name": "Isabella" }, { "id": 15, "name": "Lucas" }, { "id": 16, "name": "Mia" }, { "id": 17, "name": "Henry" }, { "id": 18, "name": "Evelyn" }, { "id": 19, "name": "Theodore" }, { "id": 20, "name": "Harper" } ]
接下来,创建一个新的状态变量 data,用于将热门名称存储在 NameList 组件中,如下所示 −
const [data, setData] = useState([])
接下来,创建一个新的状态变量 isLoading,用于存储加载状态,如下所示 −
const [isLoading, setLoading] = useState([])
接下来,使用 fetch 方法从 json 文件中获取热门名称,并将其设置为 useEffect Hooks(钩子)内的数据状态变量
useEffect(() => { setTimeout(() => { fetch("json/names.json") .then( (response) => response.json()) .then( (json) => { console.log(json); setLoading(false); setData(json); } ) }, 2000) })
这里我们有,
使用setTimout方法模拟加载过程。
使用fetch方法获取json文件。
使用json方法解析json文件。
使用setData将从json文件解析的名称设置为数据状态变量。
使用setLoading设置加载状态。
接下来,使用map方法渲染名称。在获取期间,显示加载状态。
<div> {isLoading && <span>loading...</span>} {!isLoading && data && <span>Popular names: </span>} {!isLoading && data && data.map((item) => <span key={item.id}>{item.name} </span> )} </div>
这里有,
使用isLoading显示加载状态
使用data变量显示热门名称列表
组件NameList的完整源代码如下 −
import { useState, useEffect } from "react" function NameList() { const [data, setData] = useState([]) const [isLoading, setLoading] = useState([]) useEffect(() => { setTimeout(() => { fetch("json/names.json") .then( (response) => response.json()) .then( (json) => { console.log(json); setLoading(false); setData(json); } ) }, 2000) }) return ( <div> {isLoading && <span>loading...</span>} {!isLoading && data && <span>Popular names: </span>} {!isLoading && data && data.map((item) => <span key={item.id}>{item.name} </span> )} </div> ) } export default NameList
接下来,打开浏览器并检查应用程序。它将显示加载状态,2 秒后,它将获取 json 并显示热门名称,如下所示 −


DOM 突变
useEffect Hooks(钩子)可用于使用 DOM 及其方法来操作文档。它确保其中的代码仅在 DOM 准备就绪后才执行。让我们更改我们的姓名列表应用程序并使用 DOM 突变更新页面的标题。
首先,打开 NameList 组件并根据加载状态添加文档标题,如下所示 −
useEffect(() => { if(isLoading) document.title = "Loading popular names..." else document.title = "Popular name list" setTimeout(() => { fetch("json/names.json") .then( (response) => response.json()) .then( (json) => { console.log(json); setLoading(false); setData(json);} ) }, 2000) })
这里我们使用了 DOM 对象 document.title 来更新页面的标题。
最后,打开浏览器,检查文档的标题是如何通过 DOM 操作更新的


清理函数
useEffect 可用于在从页面文档卸载组件期间删除清理函数,例如 clearInterval、removeEventListener 等。这将防止内存泄漏并提高性能。为此,我们可以创建自己的清理函数并从 useEffect 回调参数中返回它。
让我们将名称列表应用程序更改为使用 setInterval 而不是 setTimeout,然后在卸载组件期间使用 clearInterval 删除设置的回调函数。
首先,打开 NameList 组件并更新 useEffect 部分,如下所示 −
useEffect(() => { if(isLoading) document.title = "Loading popular names..." else document.title = "Popular name list" let interval = setInterval(() => { setLoading(true) fetch("json/names.json") .then( (response) => response.json()) .then( (json) => { console.log(json); setLoading(false); setData(json);} ) }, 5000) return () => { clearInterval(interval) } })
这里我们有,
使用setImterval每5秒更新一次流行名称。
在清理函数中使用clearInterval在卸载组件期间删除setInterval。
最后,打开浏览器并检查应用程序的行为。我们将看到数据每5秒更新一次。卸载组件时,将在后台调用clearInterval。
总结
useEffect是函数组件的基本功能,使组件能够使用生命周期事件。它有助于函数组件提供丰富的功能以及可预测和优化的性能。