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 />);