你好呀,正在学 React 的朋友 👋!
是不是看到 useState、useEffect 这些词就头大?
是不是写了个组件,点按钮没反应,控制台还报错?
是不是搞不清“父组件怎么把数据传给子组件”?
别怕!这篇文章 从零开始,手把手带你搞懂 React 最常用、最基础的三块内容:
- 6 个最常用的 Hooks(怎么用、什么时候用)
- 4 个超实用的 React 基础 API(不是 Hook,但天天用)
- 组件之间怎么“说话”(父子、兄弟怎么传数据)
全文无废话,全是代码 + 解释 + 场景,建议收藏,边看边敲代码!
🔹 第一部分:Hooks —— 函数组件的“超能力”
在 React 中,函数组件本来只能展示 UI,不能有状态、不能发请求。
但有了 Hooks,它就能“活”起来!
✅ 所有 Hooks 都要以
use开头(这是规则!)
✅ 只能在函数组件或自定义 Hook 里用(不能在 if 里、不能在普通函数里)
1️⃣ useState —— 让组件“记住东西”
🎯 作用:
让函数组件拥有可以变化的数据(比如计数器、输入框内容)。
💡 使用方法:
import { useState } from 'react';
function Counter() {
// 声明一个叫 count 的状态,初始值是 0
const [count, setCount] = useState(0);
return (
<div>
<p>你点了 {count} 次</p>
{/* 点击时,调用 setCount 把 count + 1 */}
setCount(count + 1)}>
+1
</div>
);
}
📌 关键点:
count是当前状态值(只读!不能直接改)setCount是修改状态的函数- 调用
setCount后,React 会自动重新渲染组件,显示新值
❌ 错误写法:
count = count + 1→ 不会更新页面!
✅ 正确写法:setCount(count + 1)
⚠️ 小技巧:如果新值依赖旧值,用函数形式更安全
// 推荐(尤其在异步或多次更新时)
setCount(prevCount => prevCount + 1);
2️⃣ useEffect —— 让组件“干点别的事”
🎯 作用:
处理副作用(side effect),比如:
- 页面加载时发请求
- 监听键盘事件
- 修改 document.title
- 订阅 WebSocket
🧠 “副作用” = 和 UI 渲染无关的操作
💡 使用方法(三种常见场景):
✅ 场景1:组件一加载就执行(类似 componentDidMount)
useEffect(() => {
console.log('组件挂载了!');
document.title = '欢迎来到我的页面';
}, []); // ← 注意这个空数组!
[]表示“只在组件第一次加载时运行一次”
✅ 场景2:某个数据变了才执行
useEffect(() => {
console.log(`用户ID变了,现在是:${userId}`);
fetchUser(userId); // 比如根据 userId 获取用户信息
}, [userId]); // ← 依赖 userId
- 只要
userId改变,这个 effect 就会重新运行
✅ 场景3:组件卸载前清理(比如取消订阅)
useEffect(() => {
const timer = setInterval(() => {
console.log('每秒打印一次');
}, 1000);
// 返回一个函数,就是“清理函数”
return () => {
clearInterval(timer); // 组件消失时,清除定时器
console.log('定时器已清除');
};
}, []);
📌 关键点:
-
依赖数组决定何时执行:
[]→ 只运行一次(挂载/卸载)[a, b]→ a 或 b 变了才运行- 不写 → 每次渲染都运行(慎用!可能死循环)
🚨 常见错误:
- 忘记加依赖 → 数据不更新
- 依赖写错 → 无限请求
✅ 解法:安装 ESLint 插件eslint-plugin-react-hooks,它会自动提醒你!
3️⃣ useRef —— 拿到真实 DOM 或存“私房钱”
🎯 作用:
- 获取真实的 HTML 元素(比如 ``)
- 存储一个不会触发重渲染的变量(类似类组件的实例属性)
💡 使用方法:
✅ 拿 DOM 元素(聚焦输入框)
import { useRef } from 'react';
function TextInput() {
const inputRef = useRef(null); // 创建一个 ref
const focusInput = () => {
inputRef.current.focus(); // 调用原生 DOM 方法
};
return (
<>
{/* 把 ref 绑定到 input 上 */}
点我聚焦输入框
);
}
✅ 存变量(比如记录渲染次数)
const renderCount = useRef(0);
// 每次渲染都 +1,但不会导致组件重新渲染!
renderCount.current = renderCount.current + 1;
console.log('渲染了', renderCount.current, '次');
📌 关键点:
ref.current是可读可写的- 改
ref.current不会触发组件重新渲染
4️⃣ useContext —— 全局传值,不用一层层 props
🎯 作用:
解决“爷爷传孙子,中间爸爸只是个传话筒”的问题(专业叫法:props drilling)
💡 使用方法(三步走):
第一步:创建 Context
// context/UserContext.js
import { createContext } from 'react';
// 创建一个 Context,初始值是 null
export const UserContext = createContext(null);
第二步:在顶层用 Provider 包裹,并传值
// App.js
import { UserContext } from './context/UserContext';
function App() {
const user = { name: '小明', id: 123 };
return (
{/* 把 user 传给所有子组件 */}
<header/>
<main/>
);
}
第三步:在任意子组件中使用
// Header.js
import { useContext } from 'react';
import { UserContext } from './context/UserContext';
function Header() {
const user = useContext(UserContext); // ← 拿到 user!
return <h1>欢迎你,{user?.name}!</h1>;
}
📌 关键点:
- 适合全局共享数据:用户信息、主题、语言等
- ❌ 不适合频繁变化的状态(会导致所有用到的组件重渲染)
5️⃣ useCallback —— 让函数“稳定不变”
🎯 作用:
防止函数在每次渲染时都生成新引用,避免子组件不必要重渲染
💡 使用场景:
当你把函数作为 prop 传给 React.memo 包裹的子组件时。
// 父组件
import { useCallback } from 'react';
function Parent() {
const [count, setCount] = useState(0);
// 用 useCallback 包裹,只有 count 变了才生成新函数
const handleClick = useCallback(() => {
console.log('点击了,当前 count 是', count);
}, [count]);
return (
<div>
<p>Count: {count}</p>
setCount(c => c + 1)}>+1
{/* 传给子组件 */}
</div>
);
}
// 子组件用 React.memo 优化
const ChildButton = React.memo(({ onClick }) => {
console.log('ChildButton 渲染了'); // 如果没用 useCallback,每次父组件更新都会打印
return 点我;
});
📌 关键点:
useCallback(fn, deps)≈useMemo(() => fn, deps)- 只有当子组件被 memo 优化时才有意义
6️⃣ useMemo —— 缓存计算结果,省电又省心
🎯 作用:
避免重复执行耗时的计算
💡 使用方法:
import { useMemo } from 'react';
function ExpensiveList({ list, filter }) {
// 只有 list 或 filter 变了,才重新过滤
const filteredList = useMemo(() => {
console.log('执行了过滤(很慢的操作)');
return list.filter(item => item.includes(filter));
}, [list, filter]);
return (
<ul>
{filteredList.map(item => <li>{item}</li>)}
</ul>
);
}
📌 关键点:
- 适合:大数据过滤、排序、复杂对象生成
- ❌ 简单计算(如
a + b)没必要用,反而增加开销
🔹 第二部分:React 基础 API(不是 Hook,但超常用)
这些是 React 提供的工具函数,帮你优化性能、组织代码。
✅ React.memo() —— “你没变就别重渲染!”
🎯 作用:
包裹一个组件,让它只有 props 变了才重新渲染
const UserInfo = React.memo(({ name, age }) => {
console.log('UserInfo 渲染了');
return <div>{name}, {age}岁</div>;
});
💡 默认做浅比较(只比第一层 props 是否相等)
⚠️ 注意:
如果 props 里有对象或函数,记得用 useMemo / useCallback 固定引用,否则浅比较会失败!
✅ React.lazy + Suspense —— 懒加载组件
🎯 作用:
按需加载组件,减少首屏 JS 体积
import { lazy, Suspense } from 'react';
// 动态导入组件
const ProfilePage = lazy(() => import('./ProfilePage'));
function App() {
return (
<div>
<nav>导航栏</nav>
{/* 用 Suspense 包裹,loading 时显示 fallback */}
加载中...</div>}>
</div>
);
}
✅ 适合:路由页面、弹窗、非首屏内容
✅ Fragment(<> )—— 无痕包裹
🎯 作用:
返回多个元素,不产生额外 DOM 节点
function ListItem() {
return (
<> {/* 等价于 */}
<dt>标题</dt>
<dd>内容</dd>
);
}
💡 在列表、表格、定义列表中特别有用!
✅ createContext —— Context 的“出生证明”
前面 useContext 已经讲过,这里再强调:
const MyContext = React.createContext(defaultValue);
defaultValue只在没有 Provider 时生效- 通常和
.Provider配合使用
🔹 第三部分:组件通信 —— 它们是怎么“说话”的?
React 遵循 “单向数据流” :数据从上往下传。
1️⃣ 父 → 子:用 props
// 父组件
function App() {
const message = "Hello from parent!";
return ;
}
// 子组件
function Child({ text }) {
return <p>{text}</p>; // 显示 "Hello from parent!"
}
✅ 最安全、最清晰的方式!
2️⃣ 子 → 父:用 回调函数(callback)
// 父组件
function App() {
const [data, setData] = useState('');
const handleChildData = (childData) => {
setData(childData); // 接收子组件传来的数据
};
return ;
}
// 子组件
function Child({ onSendData }) {
const handleClick = () => {
onSendData("我是子组件的数据"); // 调用父组件传来的函数
};
return 发送数据给父组件;
}
🧠 思想:父组件把“接收数据的方法”传给子组件,子组件调用它
3️⃣ 兄弟组件 or 跨层级:用 Context
前面已详细讲过,适合全局状态。
❌ 不要用 Context 传临时数据(比如表单某一步的值)
✅ 用它传:用户登录状态、主题色、语言偏好
4️⃣ 特殊情况:用 ref + forwardRef(命令式操作)
适用于:聚焦、播放、滚动等操作。
// 子组件
const InputBox = forwardRef((props, ref) => {
return ;
});
// 父组件
function App() {
const inputRef = useRef();
const focusInput = () => {
inputRef.current.focus(); // 调用子组件的 DOM 方法
};
return (
<>
聚焦输入框
);
}
⚠️ 仅用于操作,不要用它传数据!
🧠 终极总结:一张表搞定所有
| 名称 | 作用 | 什么时候用 | 注意事项 |
|---|---|---|---|
useState | 管理状态 | 需要可变数据(计数、输入) | 用 setX 更新,别直接改 |
useEffect | 处理副作用 | 发请求、监听、改 title | 依赖数组别漏! |
useRef | 拿 DOM / 存变量 | 聚焦、存不触发渲染的值 | current 可读写 |
useContext | 全局传值 | 多层组件共享数据 | 别放高频更新状态 |
useCallback | 缓存函数 | 传函数给 memo 子组件 | 配合 React.memo 用 |
useMemo | 缓存计算 | 大数据过滤、排序 | 简单计算别用 |
React.memo | 优化渲染 | 子组件 props 不常变 | 注意函数/对象引用 |
React.lazy | 懒加载 | 路由、大组件 | 配合 Suspense |
🌈 结语:你已经比昨天的自己更懂 React 了!
React 看似复杂,其实核心思想很简单:
UI = f(state) —— 页面是状态的函数。
只要你理解了:
- 状态怎么变(
useState) - 副作用怎么处理(
useEffect) - 组件怎么传数据(props / Context)
你就已经掌握了 90% 的日常开发!
📌 建议:把本文当“字典”,写代码卡壳时回来查一查。
✨ 彩蛋口诀:
状态用 state,副作用靠 effect,
全局用 context,DOM 用 ref 拿,
函数用 callback,计算用 memo,
父子靠 props,跨层靠 context,
懒加载用 lazy,包裹用 fragment!