React 类组件详解
一、类组件的核心定义
React 类组件是基于 ES6 Class 语法创建的 React 组件,它必须满足两个核心条件:
- 继承自
React.Component(或React.PureComponent,后续会说明差异) - 必须定义
render()方法(唯一强制要求的生命周期方法),用于返回组件要渲染的 JSX 内容
基础结构示例
jsx
import React from 'react';
// 类组件的基础写法
class Greeting extends React.Component {
// render() 方法:必须存在,负责渲染视图
render() {
// 通过 this.props 接收父组件传递的属性
return <h1>Hello, {this.props.name}!</h1>;
}
}
// 使用类组件
function App() {
return <Greeting name="React 类组件" />;
}
export default App;
二、类组件的状态(State)管理
类组件拥有自身的内部状态(State),用于存储组件自身需要维护的数据,这是类组件与早期函数组件(无 Hooks)的核心区别之一。
1. State 的初始化
有两种常用初始化方式:
- 方式1:在类的构造函数(
constructor)中初始化(传统写法,支持访问props) - 方式2:直接在类体中定义(ES7 语法,更简洁,推荐)
jsx
class Counter extends React.Component {
// 方式2:简洁初始化(推荐)
state = {
count: 0, // 初始化计数器状态
message: this.props.initMsg // 可直接访问 props
};
// 方式1:构造函数初始化(传统写法,需调用 super(props))
// constructor(props) {
// super(props); // 必须先调用父类构造函数,否则无法访问 this.props
// this.state = {
// count: 0,
// message: props.initMsg
// };
// }
render() {
return (
<div>
<p>计数:{this.state.count}</p>
<p>提示:{this.state.message}</p>
</div>
);
}
}
2. State 的更新:setState() 方法
注意:绝对不能直接修改 this.state(如 this.state.count = 1),这种写法不会触发组件重新渲染,React 要求通过 setState() 方法更新状态。
setState() 有两种用法:
- 用法1:对象式更新(适用于状态更新不依赖于当前状态)
- 用法2:函数式更新(适用于状态更新依赖于当前状态,如累加、递减)
jsx
class Counter extends React.Component {
state = { count: 0 };
// 对象式更新:不依赖当前状态
increment = () => {
this.setState({ count: this.state.count + 1 });
// 注意:setState 是异步的!若需在状态更新后执行操作,可传入第二个回调参数
// this.setState({ count: this.state.count + 1 }, () => {
// console.log("状态更新后的count:", this.state.count);
// });
};
// 函数式更新:依赖当前状态(推荐,避免异步更新导致的误差)
decrement = () => {
this.setState((prevState, props) => {
// prevState:更新前的状态;props:当前组件的 props
return { count: prevState.count - 1 };
});
};
render() {
return (
<div>
<p>计数:{this.state.count}</p>
<button onClick={this.increment}>+1</button>
<button onClick={this.decrement}>-1</button>
</div>
);
}
}
关键特性:setState() 是异步执行的,React 会批量合并状态更新以提升性能。
三、类组件的生命周期(核心知识点)
类组件的生命周期分为三个核心阶段,每个阶段对应不同的生命周期方法,用于在组件不同阶段执行特定逻辑。
1. 挂载阶段(Mount):组件首次渲染到 DOM 中
执行顺序:constructor() → render() → componentDidMount()
constructor(props):组件构造函数,用于初始化 state、绑定事件处理函数(若不使用箭头函数)render():渲染 JSX 到虚拟 DOM,此时尚未挂载到真实 DOMcomponentDidMount():组件已挂载到真实 DOM 后执行- 常用场景:发起网络请求(接口调用)、初始化第三方库(如 ECharts)、添加定时器等
jsx
class UserList extends React.Component {
state = { users: [] };
componentDidMount() {
// 组件挂载后发起接口请求
fetch('https://api.example.com/users')
.then(res => res.json())
.then(data => this.setState({ users: data }))
.catch(err => console.error(err));
}
render() {
return (
<ul>
{this.state.users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
}
2. 更新阶段(Update):组件因 props 变化或 state 更新而重新渲染
执行顺序:render() → componentDidUpdate(prevProps, prevState, snapshot)
- 触发更新的原因:
- 自身
state被setState()更新 - 父组件传递的
props发生变化 - 手动调用
forceUpdate()(不推荐,会跳过 shouldComponentUpdate 优化)
- 自身
componentDidUpdate:组件更新完成后执行- 接收三个参数:更新前的 props、更新前的 state、快照(来自 getSnapshotBeforeUpdate)
- 常用场景:根据 props 变化重新发起请求、更新第三方库配置
jsx
class UserDetail extends React.Component {
state = { user: null };
componentDidMount() {
this.fetchUser(this.props.userId);
}
// 组件更新后,若 userId 变化,重新请求用户信息
componentDidUpdate(prevProps) {
if (prevProps.userId !== this.props.userId) {
this.fetchUser(this.props.userId);
}
}
fetchUser = (id) => {
fetch(`https://api.example.com/users/${id}`)
.then(res => res.json())
.then(data => this.setState({ user: data }));
};
render() {
if (!this.state.user) return <div>加载中...</div>;
return <div>用户名:{this.state.user.name}</div>;
}
}
3. 卸载阶段(Unmount):组件从 DOM 中移除
仅一个生命周期方法:componentWillUnmount()
- 执行时机:组件即将被卸载并销毁前
- 常用场景:清理副作用,如清除定时器、取消网络请求、解绑第三方库事件等
jsx
class Timer extends React.Component {
state = { time: 0 };
timerId = null;
componentDidMount() {
// 初始化定时器
this.timerId = setInterval(() => {
this.setState(prevState => ({ time: prevState.time + 1 }));
}, 1000);
}
componentWillUnmount() {
// 组件卸载前清除定时器,防止内存泄漏
clearInterval(this.timerId);
}
render() {
return <div>已计时:{this.state.time} 秒</div>;
}
}
补充:优化型生命周期方法
shouldComponentUpdate(nextProps, nextState):更新阶段的前置钩子,返回布尔值- 返回
true(默认):允许组件更新;返回false:阻止组件更新 - 常用场景:手动优化性能,避免不必要的重新渲染
- 返回
React.PureComponent:继承自React.Component,自动实现了shouldComponentUpdate的浅比较(对比 props 和 state 的浅层属性),无需手动编写优化逻辑(注意:仅支持浅比较,深层对象变化无法检测)
四、类组件的事件处理
类组件的事件处理函数有两个注意点:
- 事件处理函数中的
this指向问题(默认情况下,类方法中的this为undefined) - 事件绑定的三种解决方案
解决方案1:箭头函数(推荐,最简洁)
jsx
class Button extends React.Component {
handleClick = () => {
// 箭头函数自动绑定 this,指向组件实例
console.log("按钮被点击", this.props.title);
};
render() {
return <button onClick={this.handleClick}>{this.props.title}</button>;
}
}
解决方案2:构造函数中绑定 this
jsx
class Button extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this); // 手动绑定 this
}
handleClick() {
console.log("按钮被点击", this.props.title);
}
render() {
return <button onClick={this.handleClick}>{this.props.title}</button>;
}
}
解决方案3:渲染时使用箭头函数(不推荐,每次渲染会创建新函数)
jsx
class Button extends React.Component {
handleClick() {
console.log("按钮被点击", this.props.title);
}
render() {
// 每次 render 都会创建新的箭头函数,可能影响性能
return <button onClick={() => this.handleClick()}>{this.props.title}</button>;
}
}
五、类组件 vs 函数组件(Hooks)
随着 React 16.8 推出 Hooks,函数组件已成为主流,但类组件仍有一定的历史使用场景,两者核心差异如下:
| 特性 | 类组件 | 函数组件(Hooks) |
|---|---|---|
| 状态管理 | 基于 this.state | 基于 useState/useReducer |
| 生命周期 | 明确的生命周期方法 | 基于 useEffect 模拟 |
this 指向 | 存在绑定问题 | 无 this,无需处理绑定 |
| 代码简洁性 | 代码冗余,模板化 | 代码简洁,逻辑内聚 |
| 复用逻辑 | 高阶组件(HOC)/Render Props | 自定义 Hooks(更优雅) |
| 性能优化 | shouldComponentUpdate/PureComponent | React.memo/useMemo/useCallback |
六、类组件的局限性(为何现在优先使用函数组件)
this指向问题:增加了学习成本和潜在的 bug- 代码冗余:生命周期方法容易导致逻辑分散(如网络请求在
componentDidMount,更新请求在componentDidUpdate) - 逻辑复用困难:高阶组件和 Render Props 会增加组件嵌套层级("嵌套地狱")
- 难以实现复杂逻辑:对于异步状态管理、上下文消费等场景,代码可读性较差
总结
- React 类组件是基于 ES6 Class 继承
React.Component的组件,必须包含render()方法 - 内部状态通过
this.state初始化,通过setState()异步更新(支持对象式和函数式两种写法) - 生命周期分为挂载、更新、卸载三个阶段,核心方法有
componentDidMount、componentDidUpdate、componentWillUnmount - 事件处理需解决
this绑定问题,推荐使用箭头函数 - 类组件已逐渐被函数组件(Hooks)替代,但在老项目维护中仍需掌握
- 核心差异:类组件依赖
this和生命周期,函数组件依赖 Hooks,代码更简洁、逻辑更易复用