React 类组件
在 React 16.8 之前,类组件是跟踪 React 组件状态和生命周期的唯一方法。函数组件被认为是“无状态”的。
随着 Hooks 的添加,函数组件现在几乎等同于类组件。它们之间的差异非常小,您可能永远不需要在 React 中使用类组件。
尽管推荐使用函数组件,但目前没有计划从 React 中移除类组件。
本节将概述如何在 React 中使用类组件。
您可以跳过本节,而是使用函数组件。
React 组件
组件是独立且可重用的代码片段。它们的作用与 JavaScript 函数相同,但它们独立工作,并通过 render() 函数返回 HTML。
组件有两种类型:类组件和函数组件,在本章中您将学习类组件。
创建类组件
创建 React 组件时,组件名称必须以大写字母开头。
组件必须包含 extends React.Component
语句,该语句创建了对 React.Component 的继承,并使您的组件可以访问 React.Component 的函数。
组件还需要一个 render()
方法,该方法返回 HTML。
示例
创建一个名为 Car
的类组件
class Car extends React.Component {
render() {
return <h2>Hi, I am a Car!</h2>;
}
}
现在您的 React 应用程序拥有一个名为 Car 的组件,它返回一个 <h2>
元素。
要在您的应用程序中使用此组件,请使用类似于常规 HTML 的语法:<Car />
示例
在“root”元素中显示 Car
组件
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Car />);
组件构造函数
如果您的组件中有一个
函数,则在组件初始化时将调用此函数。constructor()
构造函数是您初始化组件属性的地方。
在 React 中,组件属性应保留在名为 state
的对象中。
您将在本教程后面了解有关 state
的更多信息。
构造函数也是通过包含 super()
语句来尊重父组件继承的地方,该语句执行父组件的构造函数,并且您的组件可以访问父组件(React.Component
)的所有函数。
示例
在 Car 组件中创建一个构造函数,并添加一个 color 属性
class Car extends React.Component {
constructor() {
super();
this.state = {color: "red"};
}
render() {
return <h2>I am a Car!</h2>;
}
}
在 render() 函数中使用 color 属性
示例
class Car extends React.Component {
constructor() {
super();
this.state = {color: "red"};
}
render() {
return <h2>I am a {this.state.color} Car!</h2>;
}
}
Props
处理组件属性的另一种方法是使用 props
。
Props 就像函数参数一样,您将它们作为属性发送到组件中。
您将在下一章中了解有关 props
的更多信息。
示例
使用属性将颜色传递给 Car 组件,并在 render() 函数中使用它
class Car extends React.Component {
render() {
return <h2>I am a {this.props.color} Car!</h2>;
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Car color="red"/>);
构造函数中的 Props
如果您的组件具有构造函数,则 props 应始终传递给构造函数,并也通过 super()
方法传递给 React.Component。
示例
class Car extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h2>I am a {this.props.model}!</h2>;
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Car model="Mustang"/>);
组件中的组件
我们可以在其他组件中引用组件
示例
在 Garage 组件中使用 Car 组件
class Car extends React.Component {
render() {
return <h2>I am a Car!</h2>;
}
}
class Garage extends React.Component {
render() {
return (
<div>
<h1>Who lives in my Garage?</h1>
<Car />
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Garage />);
文件中的组件
React 专注于代码重用,将一些组件放在单独的文件中可能很明智。
为此,请创建一个带有 .js
文件扩展名的新文件,并将代码放入其中
请注意,文件必须首先导入 React(如前所述),并且必须以 export default Car;
语句结束。
示例
这是新文件,我们将其命名为 Car.js
import React from 'react';
class Car extends React.Component {
render() {
return <h2>Hi, I am a Car!</h2>;
}
}
export default Car;
要能够使用 Car
组件,您必须在应用程序中导入该文件。
示例
现在我们将在应用程序中导入 Car.js
文件,并且可以使用 Car
组件,就像它在这里创建的一样。
import React from 'react';
import ReactDOM from 'react-dom/client';
import Car from './Car.js';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Car />);
React 类组件状态
React 类组件具有一个内置的 state
对象。
您可能已经注意到,我们在组件构造函数部分较早地使用了 state
。
state
对象是您存储属于组件的属性值的地方。
当 state
对象更改时,组件将重新渲染。
创建 state 对象
state 对象在构造函数中初始化
示例
在构造函数方法中指定 state
对象
class Car extends React.Component {
constructor(props) {
super(props);
this.state = {brand: "Ford"};
}
render() {
return (
<div>
<h1>My Car</h1>
</div>
);
}
}
state 对象可以包含任意数量的属性
示例
指定组件所需的所有属性
class Car extends React.Component {
constructor(props) {
super(props);
this.state = {
brand: "Ford",
model: "Mustang",
color: "red",
year: 1964
};
}
render() {
return (
<div>
<h1>My Car</h1>
</div>
);
}
}
使用 state
对象
在组件中的任何位置,使用 this.state.propertyname
语法引用 state
对象
示例
在 render()
方法中引用 state
对象
class Car extends React.Component {
constructor(props) {
super(props);
this.state = {
brand: "Ford",
model: "Mustang",
color: "red",
year: 1964
};
}
render() {
return (
<div>
<h1>My {this.state.brand}</h1>
<p>
It is a {this.state.color}
{this.state.model}
from {this.state.year}.
</p>
</div>
);
}
}
更改 state
对象
要更改 state 对象中的值,请使用 this.setState()
方法。
当 state
对象中的值更改时,组件将重新渲染,这意味着输出将根据新值进行更改。
示例
添加一个带有 onClick
事件的按钮,该按钮将更改颜色属性
class Car extends React.Component {
constructor(props) {
super(props);
this.state = {
brand: "Ford",
model: "Mustang",
color: "red",
year: 1964
};
}
changeColor = () => {
this.setState({color: "blue"});
}
render() {
return (
<div>
<h1>My {this.state.brand}</h1>
<p>
It is a {this.state.color}
{this.state.model}
from {this.state.year}.
</p>
<button
type="button"
onClick={this.changeColor}
>Change color</button>
</div>
);
}
}
始终使用 setState()
方法更改 state 对象,它将确保组件知道已更新,并调用 render() 方法(以及所有其他生命周期方法)。
组件的生命周期
React 中的每个组件都有一个生命周期,您可以在其三个主要阶段中监控和操作它。
三个阶段是:挂载、更新和卸载。
挂载
挂载意味着将元素放入 DOM。
React 有四个内置方法,在挂载组件时按此顺序调用
constructor()
getDerivedStateFromProps()
render()
componentDidMount()
render()
方法是必需的,并且始终会被调用,其他方法是可选的,如果您定义了它们,它们将被调用。
constructor
在组件初始化时,constructor()
方法会被调用,这是设置初始 state
和其他初始值的自然位置。
constructor()
方法以 props
作为参数被调用,并且您应该始终在其他任何操作之前调用 super(props)
,这将初始化父级的构造函数方法,并允许组件从其父级(React.Component
)继承方法。
示例
每次您创建组件时,React 都会调用 constructor
方法
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {favoritecolor: "red"};
}
render() {
return (
<h1>My Favorite Color is {this.state.favoritecolor}</h1>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Header />);
getDerivedStateFromProps
getDerivedStateFromProps()
方法在渲染 DOM 中的元素之前被调用。
这是基于初始 props
设置 state
对象的自然位置。
它将 state
作为参数,并返回一个包含对 state
更改的对象。
下面的示例以最喜欢的颜色为“red”开始,但 getDerivedStateFromProps()
方法会根据 favcol
属性更新喜欢的颜色
示例
getDerivedStateFromProps 方法在 render 方法之前被调用
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {favoritecolor: "red"};
}
static getDerivedStateFromProps(props, state) {
return {favoritecolor: props.favcol };
}
render() {
return (
<h1>My Favorite Color is {this.state.favoritecolor}</h1>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Header favcol="yellow"/>);
render
render()
方法是必需的,并且是实际将 HTML 输出到 DOM 的方法。
示例
一个具有简单 render()
方法的简单组件
class Header extends React.Component {
render() {
return (
<h1>This is the content of the Header component</h1>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Header />);
componentDidMount
componentDidMount()
方法在组件渲染后被调用。
这是您运行需要组件已放置在 DOM 中的语句的地方。
示例
起初我最喜欢的颜色是红色,但给我一秒钟,它变成了黄色
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {favoritecolor: "red"};
}
componentDidMount() {
setTimeout(() => {
this.setState({favoritecolor: "yellow"})
}, 1000)
}
render() {
return (
<h1>My Favorite Color is {this.state.favoritecolor}</h1>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Header />);
更新
生命周期的下一个阶段是组件被更新时。
每当组件的 state
或 props
发生变化时,组件就会被更新。
React 有五个内置方法,在组件更新时按此顺序调用
getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
render()
方法是必需的,并且始终会被调用,其他方法是可选的,如果您定义了它们,它们将被调用。
getDerivedStateFromProps
在更新时,getDerivedStateFromProps
方法也会被调用。这是组件更新时调用的第一个方法。
这仍然是根据初始 props 设置 state
对象的自然位置。
下面的示例有一个按钮,它将喜欢的颜色更改为蓝色,但由于调用了 getDerivedStateFromProps()
方法,该方法使用 favcol 属性的颜色更新 state,因此喜欢的颜色仍然呈现为黄色
示例
如果组件被更新,getDerivedStateFromProps()
方法将被调用
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {favoritecolor: "red"};
}
static getDerivedStateFromProps(props, state) {
return {favoritecolor: props.favcol };
}
changeColor = () => {
this.setState({favoritecolor: "blue"});
}
render() {
return (
<div>
<h1>My Favorite Color is {this.state.favoritecolor}</h1>
<button type="button" onClick={this.changeColor}>Change color</button>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Header favcol="yellow" />);
shouldComponentUpdate
在 shouldComponentUpdate()
方法中,您可以返回一个布尔值,指定 React 是否应继续渲染。
默认值为 true
。
下面的示例显示了当 shouldComponentUpdate()
方法返回 false
时会发生什么
示例
在任何更新时停止组件渲染
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {favoritecolor: "red"};
}
shouldComponentUpdate() {
return false;
}
changeColor = () => {
this.setState({favoritecolor: "blue"});
}
render() {
return (
<div>
<h1>My Favorite Color is {this.state.favoritecolor}</h1>
<button type="button" onClick={this.changeColor}>Change color</button>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Header />);
示例
与上面的示例相同,但这次 shouldComponentUpdate()
方法返回 true
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {favoritecolor: "red"};
}
shouldComponentUpdate() {
return true;
}
changeColor = () => {
this.setState({favoritecolor: "blue"});
}
render() {
return (
<div>
<h1>My Favorite Color is {this.state.favoritecolor}</h1>
<button type="button" onClick={this.changeColor}>Change color</button>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Header />);
render
当组件更新时,当然会调用 render()
方法,它必须将 HTML 重新渲染到 DOM 中,并带有新的更改。
下面的示例有一个按钮,用于更改喜欢的颜色为蓝色
示例
点击按钮以更改组件的状态
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {favoritecolor: "red"};
}
changeColor = () => {
this.setState({favoritecolor: "blue"});
}
render() {
return (
<div>
<h1>My Favorite Color is {this.state.favoritecolor}</h1>
<button type="button" onClick={this.changeColor}>Change color</button>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Header />);
getSnapshotBeforeUpdate
在 getSnapshotBeforeUpdate()
方法中,您可以访问更新之前的 props
和 state
,这意味着即使在更新之后,您也可以检查更新之前的值。
如果存在 getSnapshotBeforeUpdate()
方法,您还应该包含 componentDidUpdate()
方法,否则您将收到错误。
下面的示例可能看起来很复杂,但它所做的只是
当组件挂载时,它以喜欢的颜色“red”渲染。
当组件挂载完成后,一个计时器会更改状态,一秒钟后,喜欢的颜色变为“yellow”。
此操作会触发更新阶段,由于此组件具有 getSnapshotBeforeUpdate()
方法,因此会执行此方法,并在空的 DIV1 元素中写入一条消息。
然后执行 componentDidUpdate()
方法,并在空的 DIV2 元素中写入一条消息
示例
使用 getSnapshotBeforeUpdate()
方法来找出更新前 state
对象的样子
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {favoritecolor: "red"};
}
componentDidMount() {
setTimeout(() => {
this.setState({favoritecolor: "yellow"})
}, 1000)
}
getSnapshotBeforeUpdate(prevProps, prevState) {
document.getElementById("div1").innerHTML =
"Before the update, the favorite was " + prevState.favoritecolor;
}
componentDidUpdate() {
document.getElementById("div2").innerHTML =
"The updated favorite is " + this.state.favoritecolor;
}
render() {
return (
<div>
<h1>My Favorite Color is {this.state.favoritecolor}</h1>
<div id="div1"></div>
<div id="div2"></div>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Header />);
componentDidUpdate
componentDidUpdate
方法在组件在 DOM 中更新后被调用。
下面的示例可能看起来很复杂,但它所做的只是
当组件挂载时,它以喜欢的颜色“red”渲染。
当组件挂载完成后,一个计时器会更改状态,颜色变为“yellow”。
此操作会触发更新阶段,由于此组件具有 componentDidUpdate
方法,因此会执行此方法,并在空的 DIV 元素中写入一条消息
示例
componentDidUpdate
方法在更新已渲染到 DOM 后被调用
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {favoritecolor: "red"};
}
componentDidMount() {
setTimeout(() => {
this.setState({favoritecolor: "yellow"})
}, 1000)
}
componentDidUpdate() {
document.getElementById("mydiv").innerHTML =
"The updated favorite is " + this.state.favoritecolor;
}
render() {
return (
<div>
<h1>My Favorite Color is {this.state.favoritecolor}</h1>
<div id="mydiv"></div>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Header />);
卸载
生命周期的下一个阶段是当组件从 DOM 中移除,或者卸载(React 喜欢这样称呼它)。
React 只有一个内置方法,在组件被卸载时会被调用
componentWillUnmount()
componentWillUnmount
componentWillUnmount
方法在组件即将从 DOM 中移除时被调用。
示例
点击按钮以删除标题
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {show: true};
}
delHeader = () => {
this.setState({show: false});
}
render() {
let myheader;
if (this.state.show) {
myheader = <Child />;
};
return (
<div>
{myheader}
<button type="button" onClick={this.delHeader}>Delete Header</button>
</div>
);
}
}
class Child extends React.Component {
componentWillUnmount() {
alert("The component named Header is about to be unmounted.");
}
render() {
return (
<h1>Hello World!</h1>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Container />);