React 17 及以前版本(主要是 16.x 到 17.x)是类组件(Class Component)为主流的时代,函数组件(Function Component)当时还没有 Hooks(Hooks 是 React 16.8 引入的,但类组件仍是主流写法)。下面按常用功能逐一介绍。
一、组件传参(Props)
父组件向子组件传递数据,通过属性(props)实现。
bash
// 父组件
class Parent extends React.Component {
render() {
return <Child name="张三" age={20} />;
}
}
// 子组件
class Child extends React.Component {
render() {
return <div>{this.props.name},今年 {this.props.age} 岁</div>;
}
}
特点:
props是只读的,子组件不能修改- 可以传递任意类型:字符串、数字、对象、函数、JSX 节点
- 通过
defaultProps设置默认值:Child.defaultProps = { age: 18 }
二、数据绑定(State)
类组件通过 this.state 管理内部数据,通过 this.setState() 更新。
bash
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
// setState 是异步的,更新会合并
this.setState({ count: this.state.count + 1 });
// 如果需要基于前一个状态更新,用函数形式
this.setState(prevState => ({ count: prevState.count + 1 }));
};
render() {
return (
<div>
<p>计数:{this.state.count}</p>
<button onClick={this.increment}>+1</button>
</div>
);
}
}
单向数据流: React 是单向绑定,数据从 state 流向视图,视图变化通过事件回调修改 state。
三、事件处理
bash
class Form extends React.Component {
handleClick = (e) => {
e.preventDefault(); // 阻止默认行为
console.log('点击了');
};
render() {
return <button onClick={this.handleClick}>点击</button>;
}
}
注意点:
- 事件名用驼峰:
onClick、onChange、onSubmit - 需要绑定
this(上面用了箭头函数,或者在constructor中this.handleClick = this.handleClick.bind(this))
四、表单与双向绑定(受控组件)
React 中没有真正的 v-model,需要手动实现"双向绑定":
bash
class InputForm extends React.Component {
state = { value: '' };
handleChange = (e) => {
this.setState({ value: e.target.value });
};
handleSubmit = (e) => {
e.preventDefault();
alert('提交:' + this.state.value);
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
<button type="submit">提交</button>
</form>
);
}
}
非受控组件(用 ref 直接操作 DOM):
bash
class Uncontrolled extends React.Component {
inputRef = React.createRef();
handleSubmit = () => {
console.log(this.inputRef.current.value);
};
render() {
return <input ref={this.inputRef} />;
}
}
五、条件渲染 & 列表渲染
bash
class ListDemo extends React.Component {
state = {
isLogin: true,
list: ['苹果', '香蕉', '橙子']
};
render() {
const { isLogin, list } = this.state;
return (
<div>
{/* 条件渲染 */}
{isLogin ? <p>欢迎回来</p> : <p>请登录</p>}
{/* 也可以用 && */}
{isLogin && <p>已登录</p>}
{/* 列表渲染,必须加 key */}
<ul>
{list.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
}
key 的重要性: 帮助 React 识别哪些元素改变了,提升 diff 效率,列表中必须提供唯一 key。
六、生命周期(Class 组件核心)
React 16.3 之后生命周期分为三个阶段:
| 阶段 | 方法 | 说明 |
|---|---|---|
| 挂载 | constructor |
初始化 state 和绑定方法 |
static getDerivedStateFromProps |
根据 props 更新 state(很少用) | |
render |
返回 JSX | |
componentDidMount |
组件挂载完成,适合发请求、操作 DOM | |
| 更新 | static getDerivedStateFromProps |
同上 |
shouldComponentUpdate |
返回 false 可阻止渲染(性能优化) | |
render |
重新渲染 | |
getSnapshotBeforeUpdate |
获取更新前 DOM 状态(很少用) | |
componentDidUpdate |
更新完成 | |
| 卸载 | componentWillUnmount |
清理工作:取消定时器、取消订阅 |
旧版(16.3 之前)还有:
componentWillMount、componentWillReceiveProps、componentWillUpdate(现已标记为不安全,不建议使用)
七、路由(React Router v4/v5)
React 17 时代主流是 React Router v5。
bash
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
// App.js
class App extends React.Component {
render() {
return (
<Router>
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
</nav>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/user/:id" component={User} />
<Route component={NotFound} /> {/* 404 */}
</Switch>
</Router>
);
}
}
// 路由传参方式
class User extends React.Component {
render() {
// URL 参数:/user/123
const { id } = this.props.match.params;
// Query 参数:/user?id=123
const query = new URLSearchParams(this.props.location.search);
const id2 = query.get('id');
return <div>用户ID:{id}</div>;
}
}
常用 API:
<Link>:声明式导航this.props.history.push('/path'):编程式导航withRouter:让非路由组件也能获取history、location、match
八、父子组件通信
1. 父 → 子:Props
bash
<Child data={this.state.data} />
2. 子 → 父:回调函数
bash
// 父
class Parent extends React.Component {
getData = (data) => {
console.log('子组件传来的:', data);
};
render() {
return <Child onSend={this.getData} />;
}
}
// 子
class Child extends React.Component {
send = () => {
this.props.onSend('子组件数据');
};
render() {
return <button onClick={this.send}>传数据给父组件</button>;
}
}
3. 兄弟组件通信:通过共同的父组件提升状态(State Lifting)
bash
class Parent extends React.Component {
state = { message: '' };
setMessage = (msg) => {
this.setState({ message: msg });
};
render() {
return (
<div>
<SiblingA onSend={this.setMessage} />
<SiblingB message={this.state.message} />
</div>
);
}
}
九、跨层级通信(Context)
避免层层传递 props("prop drilling")。
bash
// 1. 创建 Context
const ThemeContext = React.createContext('light');
// 2. 提供数据(Provider)
class App extends React.Component {
render() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 3. 消费数据(Consumer)
class ThemedButton extends React.Component {
render() {
return (
<ThemeContext.Consumer>
{theme => <button className={theme}>主题按钮</button>}
</ThemeContext.Consumer>
);
}
}
Class 组件也可以用 static contextType:
bash
class MyComponent extends React.Component {
static contextType = ThemeContext;
render() {
return <div>{this.context}</div>;
}
}
十、Refs(操作 DOM / 获取子组件实例)
bash
class AutoFocus extends React.Component {
inputRef = React.createRef();
componentDidMount() {
this.inputRef.current.focus();
}
render() {
return <input ref={this.inputRef} />;
}
}
回调 ref(旧写法):
bash
<input ref={el => this.inputEl = el} />
十一、性能优化
| 方法 | 说明 |
|---|---|
shouldComponentUpdate |
手动控制是否重新渲染 |
React.PureComponent |
自动浅比较 props 和 state |
React.memo(函数组件) |
类似 PureComponent |
| 拆分组件 | 避免不必要的渲染范围 |
bash
class ListItem extends React.PureComponent {
// 自动实现 shouldComponentUpdate 的浅比较
render() {
return <li>{this.props.name}</li>;
}
}
总结对比(React 17 vs 18+)
| 特性 | React 17 及以前 | React 18+ |
|---|---|---|
| 主流写法 | Class 组件 | Function 组件 + Hooks |
| 状态管理 | this.state / this.setState |
useState / useReducer |
| 副作用 | componentDidMount 等生命周期 |
useEffect |
| 上下文 | static contextType / <Context.Consumer> |
useContext |
| 路由 | React Router v5 | React Router v6(API 变化大) |
| 并发特性 | 无 | 有(Concurrent Features) |
如果你是在维护一个 React 17 的老项目,以上知识基本够用;如果是新项目,建议直接使用 React 18/19 + Hooks 写法。