前言:React 其实就是搭积木
很多刚接触 React 的同学,一上来就被各种概念吓到了:虚拟 DOM、生命周期、副作用... 听着头都大了。
其实,React 的核心思想超级简单,就三个字:组件化。
想象一下你在玩乐高积木。一个城堡(整个网页)是由无数个小的积木块(组件)搭起来的。有的积木块负责显示标题,有的负责显示按钮,有的负责弹窗。
今天,我们就来聊聊怎么造这些积木,以及积木之间怎么“悄悄话”(数据传递)。放心,不讲枯燥的理论,咱们只讲人话!
1. 函数组件 vs 类组件:时代的眼泪
在 React 的世界里,造积木有两种方式:函数组件和类组件。
历史背景:类组件 (Class Component)
以前(React 16.8 之前),如果积木需要有“记忆”(State),我们必须用类组件。它像是一个重装坦克,功能强大但笨重,写起来到处都是 this,让人头晕。
行业标准:函数组件 (Functional Component)
现在,函数组件才是王道!它就是一个普通的 JavaScript 函数。配合 Hooks(钩子),它变得既轻量又强大。
博主建议:如果你是 2024/2025 年开始学 React,请直接梭哈函数组件!类组件能看懂就行,主要用于维护老项目。
一图看懂区别
| 特性 | 函数组件 (Modern) | 类组件 (Legacy) |
|---|---|---|
| 定义方式 | 简单的 JS 函数 | 继承 React.Component 的类 |
| 代码量 | 极少,干净清爽 | 冗长,包含构造函数 |
| 状态管理 | useState Hook | this.state / this.setState |
| 性能 | 默认更轻量 | 需要实例化,开销稍大 |
JavaScript
// 类组件:看着就累
class HelloClass extends React.Component {
render() {
return <div>Hello, {this.props.name}</div>;
}
}
// 函数组件:清爽!
const HelloFunction = ({ name }) => {
return <div>Hello, {name}</div>;
};
2. 状态 (State) vs 属性 (Props):钱包与信封
这是新手最容易混淆的两个概念。咱们用一个生活中的比喻:
属性 (Props):爸爸给的零花钱
- 来源:父组件传给子组件的。
- 特点:只读! 就像爸爸给你的钱,你不能自己把 100 块改成 1000 块(会被打断腿),你只能花。
- 作用:用于父子组件通信。
状态 (State):自己的私房钱
- 来源:组件自己定义的。
- 特点:可变! 这是你的私有财产,你想怎么变就怎么变。
- 作用:记录组件内部的变化(比如计数器、输入框的内容)。
重点:当 State 发生变化时,组件会重新渲染(React 会重新画一遍 UI),这样页面就动起来了。
实战代码 (Hooks 写法)
JavaScript
import React, { useState } from 'react';
const Wallet = (props) => {
// useState 是 React 提供的一个 Hook
// money: 当前状态的值
// setMoney: 用来修改状态的函数(遥控器)
// useState(0): 初始值是 0
const [money, setMoney] = useState(0);
const earnMoney = () => {
// 错误写法:money = money + 10; (React 不知道你改了)
// 正确写法:使用 setMoney
setMoney(money + 10);
};
return (
<div style={{ border: '1px solid #ccc', padding: '10px' }}>
<h3>{props.owner} 的钱包</h3> {/* Props: 爸爸给的名字 */}
<p>余额: {money} 元</p> {/* State: 自己的私房钱 */}
<button onClick={earnMoney}>搬砖赚 10 块</button>
</div>
);
};
// 父组件使用
export default function App() {
return <Wallet owner="小明" />; // 传入 Props
}
3. 受控组件 vs 非受控组件:控制欲强一点比较好
在处理表单(Input)时,React 有两种流派。
受控组件 (Controlled) - 推荐
- 原理:输入框的值被 React 的 State 完全接管。
- 比喻:像是一个控制欲极强的男/女朋友。你心里想什么(Input 的 value),必须第一时间告诉 TA(Update State),TA 不同意你就不能变。
- 优点:数据不仅在 DOM 里,也在 React 的 State 里,Single Source of Truth (单一数据源),方便校验和处理。
非受控组件 (Uncontrolled)
- 原理:数据由 DOM 自己管理,React 需要的时候通过 ref 去拿。
- 比喻:像是一个放养的对象。平常不管你,只有到结婚(提交表单)的时候,才问你有多少存款。
- 优点:代码稍微少点,适合集成非 React 的第三方库。
JavaScript
// 受控组件示例(标准写法)
const ControlledInput = () => {
const [text, setText] = useState('');
return (
<input
value={text}
onChange={(e) => setText(e.target.value)} // 每次输入都同步给 React
/>
);
};
4. 高阶组件 (HOC) vs 自定义 Hooks:时代的更替
在笔记中我们看到了 HOC (Higher-Order Component) 。
简单说,HOC 就是一个函数,它吃进去一个组件,吐出来一个增强版的新组件。就像给手机套了一个防摔壳。
HOC 的痛点
虽然 HOC 曾经很流行,但它容易导致“套娃地狱”。打开 React DevTools,你会看到 WithLogging(WithAuth(WithTheme(Component))),看得密集恐惧症都犯了。
现代方案:自定义 Hooks
现在,我们更倾向于使用 Custom Hooks 来复用逻辑。它更直观,没有组件层级的嵌套。
场景:比如我们要记录组件加载的日志。
以前 (HOC) :
JavaScript
const MyComponent = withLogging(BaseComponent); // 包一层
现在 (Hooks) :
JavaScript
// 定义一个自定义 Hook
const useLogging = (componentName) => {
React.useEffect(() => {
console.log(`${componentName} 挂载了!`);
}, []);
};
// 在组件里直接用,没有任何嵌套!
const MyComponent = () => {
useLogging('MyComponent');
return <div>Hello</div>;
};
5. Context API:穿越时空的传送门
假设你的组件层级是这样的:
爷爷 -> 爸爸 -> 儿子 -> 孙子
如果你想把“爷爷”的一个数据传给“孙子”:
- 笨办法 (Props Drilling) :爷爷传给爸爸,爸爸传给儿子,儿子传给孙子... 中间的人虽然不用这个数据,但还得帮忙传,累死个人。
- 聪明办法 (Context) :建立一个传送门。爷爷直接把数据放进传送门,孙子直接从传送门里拿。
快速上手 Context
- 创建上下文 (createContext)
- 提供数据 (Provider)
- 消费数据 (useContext —— 这是 Hook 写法,比旧版 Consumer 爽多了)
JavaScript
import React, { createContext, useContext, useState } from 'react';
// 1. 创建 Context 对象
const ThemeContext = createContext();
// 2. 爷爷组件:提供数据
const GrandFather = () => {
const [theme, setTheme] = useState('dark');
return (
// 使用 Provider 包裹,value 就是要传送的数据
<ThemeContext.Provider value={{ theme, setTheme }}>
<Father />
</ThemeContext.Provider>
);
};
const Father = () => <Son />; // 爸爸不用管 theme
const Son = () => <GrandSon />; // 儿子也不用管
// 3. 孙子组件:直接获取数据
const GrandSon = () => {
// 一行代码拿到爷爷传下来的数据!
const { theme, setTheme } = useContext(ThemeContext);
return (
<div style={{ background: theme === 'dark' ? '#333' : '#fff' }}>
我是孙子,现在是 {theme} 模式
<button onClick={() => setTheme('light')}>切换亮色</button>
</div>
);
};
🛡️ 新手避坑指南
-
永远不要直接修改 State
- state.count = 1 —— React 根本不知道你改了,界面不会变。
- setState(1) —— 必须用 Setter 函数。
-
Context 虽好,不要贪杯
- Context 更新时,所有消费它的子组件都会强制渲染。如果你的 value 变化太频繁,可能会导致性能问题。对于复杂的全局状态,大型项目通常会用 Redux、Zustand 或 Jotai。
-
Hooks 只能在顶层调用
- 千万不要在 if、for 循环里写 useState 或 useEffect。React 是靠顺序来记住哪个 State 对应哪个 Hook 的,乱了顺序就崩了。
结语
恭喜你!读到这里,你已经掌握了 React 组件化开发最核心的 80% 内容。
- 函数组件让你写代码如丝般顺滑。
- State/Props 帮你管理数据流。
- Hooks 让你复用逻辑不再套娃。
- Context 帮你解决跨层级通信。
剩下的 20%,就是要在实战中不断踩坑和填坑了。拿起你的键盘,新建一个 React 项目(npm create vite@latest),试着写一个简单的计数器或者待办事项列表吧!