React 及其生命周期方法详解
概述
概述
我开始只是写关于生命周期方法的文章,但意识到关于 React 的介绍可能会有所帮助。
当我开始学习 React 时,我并没有完全理解其背后的方法论,就直接学习了它的语法。所以,或许这篇文章能帮助到其他和我一样情况的人。
虽然这绝不是一个包罗万象或详尽的介绍,但以下是我发现有助于理解这个强大的库及其生命周期方法的一些内容。
当然,您可以直接跳到生命周期。
那么...什么是 React?
React 简介
React 是一个出色的前端 JS 库,它允许您构建性能超强的单页应用程序,并且效率极高。
什么是单页应用程序?
这些 Web 应用程序仅提供单个 HTML 文件。即使用户可以导航到 5 个不同的页面,其中包含大量内容,服务器也只提供一个 HTML 文件。(React 并不局限于此,但它最出名的就是这个设置。)
这怎么可能呢?
这是通过以连续和智能的方式更新 DOM(您在浏览器中看到的内容)来实现的,同时利用 JavaScript 编写网站的大部分代码。1 个 HTML 文件和大量非常周到的 JavaScript 使网站...只是...React。
(很高兴成为第一个讲这个笑话的人。😜)
好的,但是那怎么可能呢?
除其他外,React 还有一些非常重要的技巧。
JSX
JSX 是一种 Javascript 混合语言,可让您在同一行上无缝编写 HTML、CSS 和 JavaScript,只需进行微小的语法更改。(🤯)
然后,React 将 JSX 渲染并编译为标准 HTML/CSS/JS,以供浏览器读取。
就我个人而言,直到我开始编写 JSX 并看到结构和功能结合在一起时,我才真正理解 JavaScript......
<section>
<h2>{paginationState ? "New Dev Work" : "More Dev Work"}</h2>
<div className="justify-content-around">
{projects.map(project => (
<DevProjectCard
key={project.id}
img={project.img}
title={project.title}
/>
))}
</div>
</section>
成分
组件是一种将 JSX 和纯 JS(例如 API 调用)拆分成独立文件的方法。模块化和关注点分离的概念在这里非常契合。
虽然组件的概念并非 React 独有,但它与 React 几乎等同。组件的理念是创建许多小巧精简的组件文件,每个文件包含一些特定功能的代码。然后将它们组合在一起,创建更复杂的布局。
好处是什么?组件可以在多个地方复用,并且其样式/功能可以随处迁移。从长远来看,这意味着更少的编码和维护。
在上面的例子中,<DevProjectCard />
代码块是一个组件。它代表另一个文件,其中包含决定开发项目卡片外观和功能的代码(JSX)。
道具
现在应用程序已经被拆分成多个小组件,我们需要一种在它们之间传递信息的方法。这时 Props 就派上用场了。
Props 从父组件获取信息并将其传递给子组件。
这意味着子组件可以专注于提供信息结构,而不必担心实际信息是什么。
(需要注意的是,props 只能向下传递。子组件无法将信息传递回其父组件。)
在我们的示例中,<DevProjectCard />
提供了一种在某种 UI 卡片元素中显示图片和标题的方法。但我们使用了 propsimg={}
和title={}
来传递实际的图片和标题。通过不将信息硬编码到 中<DevProjectCard />
,该组件可以在更多地方和方式中使用。
<DevProjectCard
key={project.id}
img={project.img}
title={project.title}
/>
注意:上面的示例使用.map()
数组方法创建了多个<DevProjectCard />
组件,每个组件都有唯一的图像和标题。这key={}
实际上不是一个 prop,而是 React 中动态生成映射列表时必需的方法。
状态
在 React 中处理信息和功能的另一种重要方法是使用 State。
状态允许您在组件内临时存储唯一信息。页面刷新后,状态将被删除。但在此之前,它仍然是快速控制功能和内容的有效方法。
例如,我们可能有一个按钮,用于在页面上加载更多开发项目卡片。使用状态,我们可以存储true
该按钮被点击时的值。
然后,我们可以在 HTML 标题中引用该状态,以在该状态为真或假时动态显示不同的文本。
<h2>{paginationState ? "New Dev Work" : "More Dev Work"}
虚拟 DOM
虚拟 DOM 是浏览器使用的真实 DOM 的副本。这就是我们目前讨论的所有内容的汇集之处!
真正的 DOM 更新速度非常慢,当您在布局中更改某些内容时,整个页面都需要重新绘制。
相反,React 使用虚拟 DOM 与真实 DOM 进行比较,查看是否有任何组件发生了变化。它使用状态和 props 的变化作为某些内容发生变化的信号。
如果 state 和/或 prop 发生变化,React 会使用虚拟 DOM 仅更新受影响的组件。这意味着即使只有一两个组件发生变化,真实 DOM 也无需刷新整个页面。再次强调🤯!
但它怎么知道要这么做呢?
好吧,深呼吸……所以我们在组织良好的组件中编写了强大的 JSX,这些组件通过状态控制其功能,并通过 props 与其他组件交互——所有这些都通过虚拟 DOM 为用户更新……呼。虚拟 DOM 如何知道何时检查状态和 props 的变化呢?
React 在战略点检查这些变化,这些被称为生命周期方法。🎉
生命周期方法
生命周期方法是 React 将当前代码与虚拟 DOM 和真实 DOM 进行比较的方式。它决定了哪些代码应该被评估以及在什么时间点被评估。
在进一步讨论之前,需要注意的是,React 最初是基于类的组件。这些组件处理状态并将 props 传递给仅显示内容的Presentation(或哑)组件。生命周期方法专门用于基于类的组件。
React的发布引入了一个名为Hooksversion 16.8
的新工具集。Hooks 允许你将展示性组件转换为函数式组件,这些组件拥有基于类的组件的所有功能,但通常需要的代码更少。
这个useEffect
钩子取代了所有生命周期方法,我也会最后介绍它。然而,许多应用程序仍然使用基于类的组件,因此了解它们的生命周期方法非常重要。
现在是生命周期方法时间!
以下是我最常用的三种,但还有更多不太常用的。最后,我会附上它们的文档链接,以便进一步探索。
组件挂载()
组件挂载到 DOM 后,它将直接执行其中的代码。它的一个常见用例是调用 API 获取新数据。
componentDidMount() {
axios.get('https://api.website/users')
.then(res => {
this.setState({ users: res.data });
})
.catch(error => {
console.log(error);
});
}
组件更新()
这将在组件通过状态或属性更新后直接执行代码。
一个常见的例子是调用已设置的 APIcomponentDidMount()
来查看是否有新数据。然而,这可能会导致无限循环,或者至少是意外的网络调用。
为了防止这种情况,componentDidUpdate()
提供了可选的prevState
和prevProps
参数来检查最新版本是否不同。如果相同,则代码将不会再次运行,也不会重新渲染任何内容。
componentDidUpdate(prevProps) {
if(this.props.users !== prevProps.users) {
axios.get('https://api.website/users')
.then(res => {
this.setState({ users: res.data });
})
.catch(error => {
console.log(error);
});
}
}
通常情况下,两种方法都会运行相同的代码。componentDidMount()
会进行设置并componentDidUpdate
检查更改。(这是 hooks 简化的一部分。)因此,将重复的代码放入辅助函数中并在两种方法中调用它会很有帮助。
// Funtion is called when component mounts
componentDidMount() {
this.fetchUsers()
}
// React will call the function if there is a change
componentDidUpdate(prevProps) {
if(this.props.users !== prevProps.users) {
this.fetchUsers()
}
}
// Helper Function
fetchUsers = () => {
axios.get('https://api.website/users')
.then(res => {
this.setState({ users: res.data });
})
.catch(error => {
console.log(error);
});
}
组件将卸载()
这将在组件被卸载和销毁之前调用。
它可以用来停止计时器并取消首次调用的网络请求componentDidMount()
。这有助于防止内存泄漏。
你永远不应该this.setState()
调用这个方法,因为这个组件实例永远不会被再次渲染。这会导致 React 无法正确读取状态。
在下面的例子中,我们告诉 Axios 弹出一些为处理全局错误而设置的拦截器。
componentWillUnmount() {
axios.interceptors.request.eject(this.reqInterceptor);
axios.interceptors.response.eject(this.resInterceptor);
}
钩子!(RIP 生命周期方法?)
version 16.8
如上所述,随着React的发布,引入了Hooks。基于类的组件不再需要处理状态和更新虚拟 DOM。函数式组件及其“按需导入”钩子开启了 React 编码的新时代。
在一个代码块中,钩子取代了、和useEffect
的功能。componentWillMount()
componentWillUpdate()
componentWillUnmount()
useEffect
在其最简单的形式中,useEffect
采用回调函数并在每个渲染上运行,模仿componentDidMount()
和componentDidUpdate()
方法。
每次useEffect
运行时,它都会在后台创建一个新功能,并将其与特定的渲染绑定在一起。
import React, { useEffect } from 'react';
useEffect(() => {
console.log("This runs everytime the component renders.");
});
useEffect
也接受一个数组作为第二个参数。如果留空,则组件安装时会运行一次代码。( componentDidMount()
)
useEffect(() => {
console.log("This runs when the component mounts.");
}, []);
该数组还可以保存一个或多个 props 和 state 值。React 会将上次渲染的值与这些值进行比较,如果它们没有变化,则跳过该 effect。但如果这些值已更改为上次渲染的值,则 effect 将再次运行。(就像prevState
和一样prevProps
。)
useEffect(() => {
setUsers(props.user);
}, [props.user]); // reruns when props updates
回调函数也可以返回其自身的函数。这用于清理之前运行的任何副作用,以防止内存泄漏。例如订阅网络请求。(就像componentWillUnmount()
)
useEffect(() => {
setUsers(props.user);
return = () => {
axios.interceptors.request.eject(this.reqInterceptor);
}
}, [props.user]);
useEffect
通过在同一个组件上设置多个useEffect
钩子,还可以让你分离关注点。例如,一个钩子处理用户在 props 上的变化,另一个钩子处理触发的动画。
useEffect(() => {
setUsers(props.user);
}, [props.user]);
useEffect(() => {
triggerTitleAnimation();
}, []);
你做到了!😄
坚持到最后,真棒!我们一起探讨了 React 性能如此之高的原因。
概括
-
JSX 结合了 HTML、CSS 和 JavaScript,使代码编写比以往更加无缝。
-
组件将代码分成单独的文件并使用 props 相互通信。
-
状态存储内容并可用于触发功能。
-
Props 和 State 一起用于更新虚拟 DOM,从而创造超快的浏览体验。
-
所有这些都是通过生命周期方法及其现代钩子对应物
useEffect
钩子来管理的。
希望以上内容能帮助你理清 React 的工作原理,并帮助你创作出一些酷炫的作品。祝你编程愉快!🤓
请在下面告诉我: 在 React 中工作时,您是否使用生命周期方法或钩子?useEffect
进一步阅读
React 生命周期方法文档
React Hooks 文档
React 完整指南
使用Adobe Photoshop设计的标题图像
鏂囩珷鏉ユ簮锛�https://dev.to/gedalyakrycer/react-and-its-lifecycle-methods-explained-1589