- ReactJS 教程
- ReactJS - 首页
- ReactJS - 简介
- ReactJS - 路线图
- ReactJS - 安装
- ReactJS - 特性
- ReactJS - 优点与缺点
- ReactJS - 架构
- ReactJS - 创建React应用
- ReactJS - JSX
- ReactJS - 组件
- ReactJS - 嵌套组件
- ReactJS - 使用新创建的组件
- ReactJS - 组件集合
- ReactJS - 样式
- ReactJS - 属性 (props)
- ReactJS - 使用属性创建组件
- ReactJS - props 校验
- ReactJS - 构造函数
- ReactJS - 组件生命周期
- ReactJS - 事件管理
- ReactJS - 创建一个事件感知组件
- ReactJS - 在Expense Manager APP中引入事件
- ReactJS - 状态管理
- ReactJS - 状态管理API
- ReactJS - 无状态组件
- ReactJS - 使用React Hooks进行状态管理
- ReactJS - 使用React Hooks进行组件生命周期管理
- ReactJS - 布局组件
- ReactJS - 分页
- ReactJS - Material UI
- ReactJS - Http客户端编程
- ReactJS - 表单编程
- ReactJS - 受控组件
- ReactJS - 非受控组件
- ReactJS - Formik
- ReactJS - 条件渲染
- ReactJS - 列表
- ReactJS - Keys
- ReactJS - 路由
- ReactJS - Redux
- ReactJS - 动画
- ReactJS - Bootstrap
- ReactJS - Map
- ReactJS - 表格
- ReactJS - 使用Flux管理状态
- ReactJS - 测试
- ReactJS - CLI命令
- ReactJS - 构建和部署
- ReactJS - 示例
- Hooks
- ReactJS - Hooks简介
- ReactJS - 使用useState
- ReactJS - 使用useEffect
- ReactJS - 使用useContext
- ReactJS - 使用useRef
- ReactJS - 使用useReducer
- ReactJS - 使用useCallback
- ReactJS - 使用useMemo
- ReactJS - 自定义Hooks
- ReactJS 高级
- ReactJS - 可访问性
- ReactJS - 代码分割
- ReactJS - Context
- ReactJS - 错误边界
- ReactJS - 转发Refs
- ReactJS - Fragments
- ReactJS - 高阶组件
- ReactJS - 与其他库集成
- ReactJS - 性能优化
- ReactJS - Profiler API
- ReactJS - Portals
- ReactJS - 无ES6 ECMAScript的React
- ReactJS - 无JSX的React
- ReactJS - 调和
- ReactJS - Refs和DOM
- ReactJS - Render Props
- ReactJS - 静态类型检查
- ReactJS - Strict Mode
- ReactJS - Web Components
- 附加概念
- ReactJS - 日期选择器
- ReactJS - Helmet
- ReactJS - 行内样式
- ReactJS - PropTypes
- ReactJS - BrowserRouter
- ReactJS - DOM
- ReactJS - 走马灯
- ReactJS - 图标
- ReactJS - 表单组件
- ReactJS - 参考API
- ReactJS 有用资源
- ReactJS - 快速指南
- ReactJS - 有用资源
- ReactJS - 讨论
ReactJS - 使用useEffect
React 提供了useEffect 用于在组件中执行副作用。一些副作用如下:
从外部来源获取数据并更新渲染内容。
渲染后更新DOM元素。
订阅
使用计时器
日志记录
在基于类的组件中,这些副作用是使用生命周期组件完成的。因此,useEffect hook 是以下生命周期事件的替代方案。
componentDidMount - 首次渲染完成后触发。
componentDidUpdate - 由于属性或状态更改导致渲染更新后触发。
componentWillUnmount - 组件销毁期间卸载渲染内容后触发。
让我们在本节学习如何在组件中使用effect hook。
useEffect 的签名
useEffect 的签名如下:
useEffect( <update function>, <dependency> )
其中,更新函数的签名如下:
{ // code return <clean up function> }
这里:
更新函数 - 更新函数是在每个渲染阶段后执行的函数。这对应于componentDidMount 和componentDidUpdate 事件。
依赖项 - 依赖项是一个数组,其中包含函数所依赖的所有变量。指定依赖项对于优化 effect hook 至关重要。通常,更新函数在每次渲染后都会被调用。有时不需要在每次渲染时都运行更新函数。让我们考虑一下,我们正在从外部来源获取数据并在渲染阶段之后更新它,如下所示:
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 变量更新时,组件都会重新渲染。但是,正如您所看到的,我们不需要在每次更新toggle状态时都运行定义的effect。为了解决这个问题,我们可以传递一个空依赖项,如下所示:
const [data, setDate] = useState({}) const [toggle, setToggle] = useState(false) const [id, setID] = useState(0) useEffect( () => { fetch('/data/url/', { id: id }).then( fetchedData => setData(fetchedData) ) }, [])
上面的代码只会在第一次渲染后运行一次 effect。尽管它会修复问题,但 effect 必须在每次id更改时运行。为了实现这一点,我们可以将id作为 effect 的依赖项包含在内,如下所示:
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])
这将确保 effect 只会在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。这将帮助我们为每个副作用编写一个函数,并将其设置为单独的 effect。
每个 hook 将按照声明的顺序运行。开发者应确保 effect 的声明顺序正确。
依赖项特性可用于提高副作用的性能和正确性。
清理函数可以防止内存泄漏和不必要的事件触发。
使用 effect 获取数据
在本节中,让我们创建一个应用程序,该应用程序将从外部来源获取数据并使用useEffect hook 渲染它。
首先,创建一个新的React应用程序,并使用以下命令启动它。
create-react-app myapp cd myapp npm start
接下来,在组件文件夹下创建一个React组件NameList(src/components/NameList.js)
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 hook内的data状态变量中。
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文件中解析的名称设置为data状态变量。
使用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 hook 可用于使用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是函数组件的重要特性,使组件能够使用生命周期事件。它帮助函数组件提供具有可预测性和优化性能的丰富功能。