React钩子在功能组件的简单性,性能和可扩展性上对我们来说更为流行。为了更好地了解钩子,我们必须知道反应组件的生命周期。我们将冒险通过React组件的生命周期到达目的地钩。 React组件的生命周期是指在组件的创建和更新期间以特定顺序调用的一系列方法。反应组件的生命周期中有三个主要阶段:
- 安装:首先创建组件并插入DOM。 时,此阶段发生
- 更新:此阶段发生在组件的状态或道具发生变化时。
- 卸载:此阶段发生在从DOM中删除组件时。
所有这些阶段都有一些方法,使我们能够控制组件生命周期中各个点发生的事情。所有这些东西在技术上都属于基于类的组件。我们不能在功能组件中使用这些生命周期方法。这是否意味着我们无法在功能组件中拥有生命周期?
答案是。我们可以使用某些钩子在功能组件中使用生命周期。在React 16.8中,引入了钩子。现在,我们可以在功能组件中使用这些生命周期方法的抽象版本。但是问题仍然存在于16.8的反应版本是如何的? React 16.8之前的所有版本都具有基于状态的2种类型的组件。
- 基于类的状态组件
- 无状态功能组件
React中的功能组件最初仅用于呈现视图或UI元素。在对16.8进行反应之前,功能组件是无状态的,无法管理自己的状态。它们只是接收道具并返回JSX元素的JavaScript功能。 React钩被引入React 16.8中,以弥合功能和类组件之间的差距,在功能组件中提供了状态管理和生命周期方法,而无需类别。
每个阶段中都有许多方法。如果我们在图片中一起记下它:
我们可以通过以下方式写下它:
安装:
- constructor()
- static getDerivedStateFromprops()
- render()
- componentdidmount()
更新:
- static getDerivedStateFromprops()
- showscomponentupdate()
- render()
- getSnapShotBoreUpdate()
- componentdidupdate()
卸载:
- componentwillunmount()
除了这些方法外,还有一些其他方法可用于处理错误并更新组件的状态。
为了在功能组件中处理所有这些方法,有一些等效的生命周期钩。内置的钩子是:
-
usestate():在功能组件中添加状态。它以初始状态值作为参数,并返回包含当前状态的数组和更新状态的函数。
-
useeffect():在功能组件中执行副作用。它需要两个参数:第一个参数是执行副作用的回调函数,第二个参数是可选的依赖项数组,该数组告诉何时需要重新运行效果。
-
usecontext():访问功能组件中的反应上下文。它将上下文对象作为参数并返回当前上下文值。
-
useref():在功能组件中创建ref。它返回一个可变的对象,该对象具有可用于存储任何值的当前属性。
-
usememo():记住功能的结果,以免在每个渲染上重新计算。它需要两个参数:返回值的函数,以及一系列依赖项并返回记忆的值。
-
usecallback():与usememo钩相同,但返回记忆的功能而不是值。
以及更多。
重要的是要知道,虽然钩子提供了与生命周期方法的等效功能,但它们通常以不同的方式进行。这意味着我们可能需要在使用React Hook而不是类组件时更改我们的写作组件的方法。
等效性可以显示为:
可以使用2个钩子一起实现许多其他方法;就像使用usestate()和useFect()可以实现getDerivedStateFromProps()一样,可以使用usestate()和useLayouteffect()等。
现在我们将一一漫步。
usstate()
状态是反应中的关键概念,代表组件的内部数据。组件本身可以随着时间的推移而更改,以响应用户操作或其他事件。通过更新组件的状态,该组件可以重新渲染并根据新数据更新其外观或行为。
首先,我们要编写一个基于类的组件来定义状态计数器。
import React from "react";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
this.setCount = this.setCount.bind(this);
}
setCount() {
this.setState((prevState) => ({ count: prevState.count + 1 }));
}
render() {
return (
<div>
<button onClick={this.setCount}>Click me</button>
<p>Count: {this.state.count}</p>
</div>
);
}
}
export default App;
在此示例中,组件是使用类关键字定义的,并扩展了React库提供的React.component类。组件的状态在构造函数方法中初始化,计数属性设置为0。计数状态属性在组件的构造函数中初始化为0。可以使用this.state.count在渲染方法中访问计数的当前值。
要更新状态,我们可以使用React提供的SetState方法。例如,要将计数属性增加1,我们将在事件处理程序中调用This.setState({count:this.state.count + 1})。
使用Usestate Hook在功能组件中处理同一件事。
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(prevCount => prevCount + 1);
}
return (
<div>
<button onClick={handleClick}>Click me</button>
<p>Count: {count}</p>
</div>
);
}
export default App;
在上一个类组件示例中,使用组件的构造函数和setState方法管理状态。
在功能组件示例中,使用USESTATE挂钩来声明计数状态变量和SetCount Updater函数。此挂钩通过提供一种方便的方式来声明和更新功能组件中的状态来简化状态管理。
USESTATE HONK以参数为初始值,并返回一个具有两个值的数组:当前状态值和更新程序函数。在示例中,计数的初始值为0。
调用HandleClick函数时,它通过调用具有新计数值的SetCount函数来更新计数状态值。这触发了具有更新的计数值的组件的重新渲染。
useeffect()
使用效果挂钩复制了3种反应生命周期方法的行为:componentdidmount,componentDidupdate和componentWillunMount。首先,我们要看基于类的组件。
import React from "react";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
console.log("Component mounted");
}
componentDidUpdate(prevProps, prevState) {
if (this.state.count !== prevState.count || this.props !== prevProps) {
console.log("Component updated");
}
}
componentWillUnmount() {
console.log("Component unmounted");
}
handleClick() {
this.setState((prevState) => ({ count: prevState.count + 1 }));
}
render() {
return (
<div>
<button onClick={this.handleClick}>Click me</button>
<p>Count: {this.state.count}</p>
</div>
);
}
}
export default App;
在此示例中,componentDidmount方法在安装组件时运行(即,在第一个渲染之后)。我们将消息记录到控制台,以指示组件已安装。
componentDidupdate方法每当更新组件时运行。我们检查计数状态变量是否已从上一个状态发生了变化,还是自上一个道具以来的道具发生了变化,如果任何一个条件都是正确的,我们将消息记录到控制台,以指示组件已更新。
当组件被卸载时,componentWillunMount方法将运行。我们将消息记录到控制台,以指示该组件已被卸载。
最后,我们具有一个处理的功能,该功能在单击按钮时会更新计数状态变量。这会触发组件的重新渲染,如果计数状态变量已更改,则会导致componentDidupdate方法运行。
现在,为了实现与基于类的组件相同的行为,我们将在功能组件中使用使用效果钩。
import React, { useState, useEffect } from "react";
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("Component mounted");
return () => {
console.log("Component unmounted");
};
}, []);
useEffect(() => {
console.log("Count updated");
}, [count]);
function handleClick() {
setCount((prevCount) => prevCount + 1);
}
return (
<div>
<button onClick={handleClick}>Click me</button>
<p>Count: {count}</p>
</div>
);
}
export default App;
在此示例中,我们使用两个使用效果挂钩来复制ComponentDidMount,ComponentDidupDate和ComponentWillunMount LifeCepycle方法的行为。
在安装组件时(即,在第一个渲染之后)时,第一个使用效果挂钩运行。我们将消息记录到控制台,以指示组件已安装。此使用效果挂钩还返回一个函数,该函数将在组件被卸载时被调用。在这种情况下,我们正在将消息记录到控制台,以指示该组件已被卸载。空依赖性数组[]可确保该效果仅在组件安装时运行一次。
每当计数状态变量更改(即每个更新之后)时,第二个使用效果挂钩就运行。我们将消息记录到控制台,以指示计数已更新。计数依赖性数组[count]可确保此效果仅在计数状态变量更改时运行。
最后,我们具有一个处理的功能,该功能在单击按钮时会更新计数状态变量。这会触发组件的重新渲染,如果计数状态变量已更改,则会导致第二个使用效果挂钩运行。
是没有交汇处的()
usememo hook与sysemponentupdate()方法的作用一样,主要用于记忆一个值,以免在每个渲染中运行。以下显示了在基于类的组件中使用showscomponentupdate()的示例。
import React, { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = {
count1: 0,
count2: 0,
expensiveValue: this.expensiveCalculation(0)
};
this.handleClick1 = this.handleClick1.bind(this);
this.handleClick2 = this.handleClick2.bind(this);
this.expensiveCalculation = this.expensiveCalculation.bind(this);
}
shouldComponentUpdate(nextProps, nextState) {
return (
nextState.count1 !== this.state.count1 ||
nextState.count2 !== this.state.count2
);
}
componentDidUpdate(prevProps, prevState) {
if (prevState.count1 !== this.state.count1) {
this.setState({
expensiveValue: this.expensiveCalculation(this.state.count1)
});
}
}
handleClick1() {
this.setState((prevState) => ({
count1: prevState.count1 + 1
}));
}
handleClick2() {
this.setState((prevState) => ({
count2: prevState.count2 + 1
}));
}
expensiveCalculation(num) {
console.log("Calculating...");
for (let i = 0; i < 1000000000; i++) {
num += 1;
}
return num;
}
render() {
const { count1, count2, expensiveValue } = this.state;
return (
<div>
<div>
<h2>Counter 1: {count1}</h2>
<button onClick={this.handleClick1}>Add Me</button>
</div>
<div>
<h2>Counter 2: {count2}</h2>
<button onClick={this.handleClick2}>Add Me</button>
</div>
<div>
<h2>Expensive Value:</h2>
{expensiveValue}
</div>
</div>
);
}
}
export default App;
在此示例中,ExpensiveCalculation函数执行了耗时的计算,模拟了难以计算值并需要大量资源的情况。因此,我们使用showsComponentUpdate方法来优化渲染,并且仅在Count1状态更改时重新计算昂贵的值。
shoreComponentUpdate方法将当前的count1状态与下一个count1状态进行比较,并且仅允许函数消耗时,如果它们不同,则运行它们。这避免了何时不更改Count1状态时不必要的计算。
我们还使用componentDidupdate生命周期方法来执行昂贵的计算并相应地更新状态。我们仅在Count1状态更改时执行此计算。
使用USEMEMO钩进行相同的优化。以下将转换的版本显示为功能组件:
import React from "react";
import { useState, useMemo } from "react";
function App() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
const expensiveValue = useMemo(() => expensiveCalculation(count1), [count1]);
function handleClick1() {
setCount1((prevCount) => prevCount + 1);
}
function handleClick2() {
setCount2((prevCount) => prevCount + 1);
}
function expensiveCalculation(num) {
console.log("Calculating...");
for (let i = 0; i < 1000000000; i++) {
num += 1;
}
return num;
}
return (
<div>
<div>
<h2>Counter 1: {count1}</h2>
<button onClick={handleClick1}>Add Me</button>
</div>
<div>
<h2>Counter 2: {count2}</h2>
<button onClick={handleClick2}>Add Me</button>
</div>
<div>
<h2>Expensive Value:</h2>
{expensiveValue}
</div>
</div>
);
}
export default App;
在这里,仅当Count1更改时,使用USEMEMO挂钩才能计算出支出。它需要两个参数:返回要记忆的值的函数(在这种情况下,是expensiveCalculation函数)和一系列依赖关系(在这种情况下,[count1])。每当Count1更改时,该函数会重新执行并更新昂贵的Value。
这意味着,如果Count1状态没有更改,则将使用ExpensiveCalculation的缓存结果而不是重新计算。
我们可以在控制台日志中看到优化的相同效果。仅在计数状态变化时才会重新计算出支出估算函数,并且在由于其他状态变化而重新呈现组件时不会重新计算。
以这种方式,所有其他钩子都以某种方式复制了在符合所有三个阶段的基于类的组件中均盛行的生命周期方法。钩子的引入消除了基于类的组件中的许多冗长,使代码更容易,更简单地编写和读取。