ReactJS - 使用 useEffect



React 提供了 useEffect 来在组件中执行副作用。一些副作用如下 -

  • 从外部源获取数据并更新呈现的内容。
  • 渲染后更新 DOM 元素。
  • 订阅
  • 使用计时器
  • 伐木

在基于类的组件中,这些副作用是使用生命周期组件完成的。因此,useEffect 钩子是下面提到的生命周期事件的效果替换。

  • componentDidMount - 首次渲染完成后触发。
  • componentDidUpdate − 由于属性或状态更改而更新渲染后触发。
  • componentWillUnmount - 在组件销毁期间卸载渲染内容后触发。

在本章中,让我们学习如何使用效果钩子。

使用效果签名

useEffect的签名如下 -


 useEffect( <update function>, <dependency> )

其中,update函数的签名如下:


{
	 	// code
	 	return <clean up function>
}

这里

更新函数 - 更新函数是在每个渲染阶段之后要执行的函数。这对应于 componentDidMount 和 componentDidUpdate 事件

依赖性 - 依赖性是一个数组,其中包含函数所依赖的所有变量。指定依赖关系对于优化效果钩子非常重要。通常,每次渲染后都会调用更新函数。有时没有必要在每次渲染时渲染更新函数。让我们考虑一下,我们正在从外部源获取数据,并在渲染阶段之后更新它,如下所示 -


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

每当数据和切换变量更新时,组件将重新渲染。但正如你所看到的,我们不需要在每次更新切换状态期间运行定义的效果。为了解决这个问题,我们可以传递一个空的依赖项,如下所示 -


const [data, setDate] = useState({})
const [toggle, setToggle] = useState(false)
const [id, setID] = useState(0)
useEffect( () => {
	 	fetch('/data/url/', { id: id }).then( fetchedData => setData(fetchedData) )
}, [])

上述代码在第一次渲染后只会运行一次效果。即使它会修复 issus,也必须在每次更改 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])

让我们创建一个完整的应用程序,以便在后面的部分中理解清理功能。

效果钩的特点

效果钩的一些显着特征如下 -

  • React 允许在函数组件中使用多个效果钩子。这将帮助我们为每个副作用编写一个函数,并将其设置为单独的效果。
  • 每个钩子都将按照其声明的顺序运行。开发人员应确保正确声明了效果的顺序。
  • 依赖性功能可用于提高性能和副作用的正确工作。
  • 清理功能可防止内存泄漏和不必要的事件触发。

使用效果获取数据

让我们创建一个应用程序,它将从外部源获取数据,并在本节中使用 useEffect 钩子渲染它。

首先,创建一个新的 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"
	 	}
]

接下来,创建一个新的状态变量,数据以在NameList组件中存储流行名称,如下所示 -


 const [data, setData] = useState([])

接下来,创建一个新的状态变量 isLoading 来存储加载状态,如下所示 -


 const [isLoading, setLoading] = useState([])

接下来,使用 fetch 方法从 json 文件中获取流行名称,并将其设置为 useEffect 钩子内的数据状态变量


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并显示流行名称,如下所示 -

Fetching Data using Effect

Fetching Data using Effect

DOM 突变

useEffect 钩子可用于使用 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操作来更新的

DOM Mutations

DOM Mutations

清理功能

useEffect 可用于在从页面文档中卸载组件时删除清理函数,例如 clearInterval、removeEventListener 等。这将防止内存泄漏并提高性能。为此,我们可以创建自己的清理函数,并从 useEffect 回调参数返回它。

让我们更改名称列表应用程序,使用 setInterval 而不是 setTimeout,然后使用 clearInterval 在卸载组件期间删除 set 回调函数。

首先,打开 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 是函数组件的基本特性,它使组件能够使用生命周期事件。它有助于功能组件提供丰富的功能,并具有可预测和优化的性能。