function Welcome(props) {
return Hello, {props.name}
;
}
**特点**:
* 简洁:纯函数,没有生命周期方法和 this
* 无状态(Hooks 之前):只能接收 props,不能管理自己的 state
* 性能:没有实例化开销,渲染更快
**使用箭头函数**:
const Welcome = (props) => {
return Hello, {props.name}
;
};
// 简写形式(隐式返回)
const Welcome = (props) => Hello, {props.name}
;
// 解构 props
const Welcome = ({ name }) => Hello, {name}
;
===== 5.2 类组件 =====
类组件使用 ES6 的 class 语法定义,继承自 React.Component。
import React from 'react';
class Welcome extends React.Component {
render() {
return Hello, {this.props.name}
;
}
}
**特点**:
* 可以使用 state 管理组件内部状态
* 可以使用生命周期方法
* 有 this 关键字
* 需要 render() 方法返回 JSX
===== 5.3 组件组合 =====
组件可以相互组合,形成组件树。
function Welcome(props) {
return Hello, {props.name}
;
}
function App() {
return (
);
}
**提取组件**:
当 UI 的一部分被多次使用,或者自身足够复杂时,可以将其提取为独立的组件。
// 原始代码
function Comment(props) {
return (
{props.author.name}
{props.text}
{formatDate(props.date)}
);
}
// 提取 Avatar 组件
function Avatar(props) {
return (
);
}
// 提取 UserInfo 组件
function UserInfo(props) {
return (
{props.user.name}
);
}
// 重构后的 Comment 组件
function Comment(props) {
return (
{props.text}
{formatDate(props.date)}
);
}
===== 5.4 Props 详解 =====
Props(properties 的缩写)是组件之间传递数据的方式。
**传递 props**:
function App() {
return ;
}
**接收 props**:
function User(props) {
return (
姓名:{props.name}
年龄:{props.age}
{props.isAdmin ? '管理员' : '普通用户'}
);
}
// 使用解构
function User({ name, age, isAdmin }) {
return (
姓名:{name}
年龄:{age}
{isAdmin ? '管理员' : '普通用户'}
);
}
**Props 的只读性**:
组件不能修改自己的 props。props 对于组件来说是只读的。
// 错误!props 是只读的
function Welcome(props) {
props.name = 'Hello'; // 错误!
return {props.name}
;
}
===== 5.5 默认 Props =====
为 props 设置默认值:
**函数组件**:
function Button({ text = '点击', type = 'button' }) {
return ;
}
// 或者
function Button(props) {
const { text = '点击', type = 'button' } = props;
return ;
}
**类组件**:
class Button extends React.Component {
static defaultProps = {
text: '点击',
type: 'button'
};
render() {
return ;
}
}
// 或者在组件外部
Button.defaultProps = {
text: '点击',
type: 'button'
};
===== 5.6 PropTypes 类型检查 =====
PropTypes 用于运行时类型检查。
安装:
npm install prop-types
使用:
import PropTypes from 'prop-types';
function User({ name, age, email }) {
return (
{name}
{age}
{email}
);
}
User.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
email: PropTypes.string,
isAdmin: PropTypes.bool,
hobbies: PropTypes.array,
address: PropTypes.object,
onClick: PropTypes.func,
element: PropTypes.element,
nodes: PropTypes.node,
user: PropTypes.instanceOf(User),
shape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),
oneOf: PropTypes.oneOf(['News', 'Photos']),
oneOfType: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
arrayOf: PropTypes.arrayOf(PropTypes.number),
objectOf: PropTypes.objectOf(PropTypes.number),
any: PropTypes.any
};
User.defaultProps = {
age: 18
};
**TypeScript 替代方案**:
如果使用 TypeScript,可以使用接口定义 props 类型,获得编译时类型检查。
===== 5.7 children prop =====
children 是一个特殊的 prop,用于在组件标签之间传递内容。
function Card({ title, children }) {
return (
{title}
{children}
);
}
// 使用
function App() {
return (
这是卡片的内容
);
}
**children 的类型**:
* React 元素
* 字符串或数字
* 数组(Fragment)
* 函数(render props)
* null、undefined、boolean(不渲染)
===== 5.8 render props 模式 =====
render props 是一种在 React 组件之间使用一个值为函数的 prop 共享代码的技术。
class MouseTracker extends React.Component {
state = { x: 0, y: 0 };
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
});
};
render() {
return (
{this.props.render(this.state)}
);
}
}
// 使用
function App() {
return (
(
鼠标位置:({x}, {y})
)} />
);
}
===== 5.9 高阶组件(HOC) =====
高阶组件是一个函数,接收一个组件并返回一个新的组件。
function withLogger(WrappedComponent) {
return class extends React.Component {
componentDidMount() {
console.log('Component mounted:', WrappedComponent.name);
}
render() {
return ;
}
};
}
// 使用
const EnhancedComponent = withLogger(MyComponent);
**常见的 HOC 用途**:
* 权限控制
* 日志记录
* 数据获取
* 样式增强
===== 5.10 组件的组织方式 =====
**按功能组织**:
import { useState } from 'react';
function Counter() {
// 声明一个叫 "count" 的 state 变量,初始值为 0
const [count, setCount] = useState(0);
return (
你点击了 {count} 次
);
}
**解构赋值说明**:
`const [count, setCount] = useState(0)` 是数组解构:
- count: 当前状态值
- setCount: 更新状态的函数
**函数式更新**:
当新状态依赖于旧状态时,使用函数式更新可以避免闭包问题。
function Counter() {
const [count, setCount] = useState(0);
// 正确:使用函数式更新
const increment = () => {
setCount(prevCount => prevCount + 1);
};
return (
{count}
);
}
**对象状态**:
当 state 是对象时,更新时需要展开旧值。
function UserForm() {
const [user, setUser] = useState({
name: '',
age: 0,
email: ''
});
const handleChange = (field, value) => {
setUser(prevUser => ({
...prevUser, // 展开旧值
[field]: value // 更新特定字段
}));
};
return (
handleChange('name', e.target.value)}
placeholder="姓名"
/>
);
}
**延迟初始化**:
当初始值计算开销较大时,可以传入函数进行延迟初始化。
function ExpensiveComponent() {
// 只会在首次渲染时执行 computeExpensiveValue
const [value, setValue] = useState(() => computeExpensiveValue());
// ...
}
===== 5.14 useEffect - 副作用管理 =====
useEffect 是 React Hooks 中最重要、最基础的 Hook 之一,用于在函数组件中执行副作用操作。
**基本语法**:
import { useEffect } from 'react';
function Example() {
useEffect(() => {
// 副作用逻辑
console.log('组件挂载或更新');
// 可选的清理函数
return () => {
console.log('清理工作');
};
}, [/* 依赖数组 */]);
return 示例组件;
}
**执行时机**:
// 1. 每次渲染后都执行
useEffect(() => {
console.log('每次渲染都会执行');
});
// 2. 只在挂载时执行(空依赖数组)
useEffect(() => {
console.log('只在挂载时执行');
fetchData();
}, []);
// 3. 依赖变化时执行
useEffect(() => {
console.log(`count 变化为: ${count}`);
document.title = `点击了 ${count} 次`;
}, [count]);
**清理函数(Cleanup)**:
function ChatRoom({ roomId }) {
useEffect(() => {
// 建立连接
const connection = createConnection(roomId);
connection.connect();
console.log(`连接到房间: ${roomId}`);
// 返回清理函数
return () => {
connection.disconnect();
console.log(`断开房间: ${roomId} 的连接`);
};
}, [roomId]);
return 聊天房间: {roomId};
}
**数据获取模式**:
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let isCancelled = false;
const controller = new AbortController();
async function fetchUser() {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`, {
signal: controller.signal
});
const data = await response.json();
if (!isCancelled) {
setUser(data);
setError(null);
}
} catch (err) {
if (!isCancelled && err.name !== 'AbortError') {
setError(err.message);
}
} finally {
if (!isCancelled) {
setLoading(false);
}
}
}
fetchUser();
return () => {
isCancelled = true;
controller.abort();
};
}, [userId]);
if (loading) return 加载中...;
if (error) return 错误: {error};
return (
{user.name}
{user.email}
);
}
**useEffect 与 useLayoutEffect**:
import { useLayoutEffect } from 'react';
function MeasureExample() {
const divRef = useRef(null);
const [width, setWidth] = useState(0);
useLayoutEffect(() => {
// 在浏览器绘制前测量 DOM
const { width } = divRef.current.getBoundingClientRect();
setWidth(width);
}, []);
return (
宽度: {width}px
);
}
- useEffect:在浏览器绘制完成后异步执行,不会阻塞视觉更新
- useLayoutEffect:在浏览器绘制之前同步执行,会阻塞视觉更新
绝大多数情况下应该使用 useEffect,只有在需要同步测量/修改 DOM 时才使用 useLayoutEffect。
===== 5.15 useRef - DOM 引用与持久化值 =====
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数。这个对象在组件的整个生命周期内保持不变。
**访问 DOM 元素**:
import { useRef } from 'react';
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => {
// 直接访问 DOM 元素
inputRef.current.focus();
};
return (
);
}
**保存上一次的值**:
function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
// 在渲染后保存当前值,供下一次渲染使用
prevCountRef.current = count;
});
const prevCount = prevCountRef.current;
return (
当前: {count}, 上一次: {prevCount}
);
}
**保存定时器 ID**:
function Timer() {
const [seconds, setSeconds] = useState(0);
const [isRunning, setIsRunning] = useState(false);
const intervalRef = useRef(null);
const start = () => {
if (!isRunning) {
setIsRunning(true);
intervalRef.current = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
}
};
const stop = () => {
if (isRunning) {
setIsRunning(false);
clearInterval(intervalRef.current);
}
};
const reset = () => {
stop();
setSeconds(0);
};
useEffect(() => {
// 组件卸载时清理定时器
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, []);
return (
{seconds} 秒
);
}
**转发 ref(forwardRef)**:
import { forwardRef, useRef } from 'react';
// 子组件转发 ref 到内部 DOM 元素
const FancyInput = forwardRef((props, ref) => {
return ;
});
// 父组件使用
function Parent() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
);
}
**useImperativeHandle 自定义暴露的实例值**:
import { forwardRef, useRef, useImperativeHandle } from 'react';
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef(null);
// 自定义暴露给父组件的方法
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
clear: () => {
inputRef.current.value = '';
},
getValue: () => {
return inputRef.current.value;
}
}));
return ;
});
// 父组件使用
function Parent() {
const fancyInputRef = useRef(null);
const handleClick = () => {
fancyInputRef.current.focus();
console.log(fancyInputRef.current.getValue());
fancyInputRef.current.clear();
};
return (
);
}
===== 5.16 useCallback - 缓存回调函数 =====
useCallback 返回一个 memoized 回调函数。只有当依赖项发生变化时,才会返回新的函数。
**基本用法**:
import { useCallback } from 'react';
function Parent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// 有 useCallback:只在 count 变化时创建新函数
const handleClick = useCallback(() => {
console.log('count:', count);
}, [count]);
return (
setText(e.target.value)} />
);
}
**配合 React.memo 使用**:
import { memo, useCallback, useState } from 'react';
// 子组件使用 React.memo 进行浅比较
const Child = memo(({ onClick, label }) => {
console.log(`${label} 渲染`);
return ;
});
function Parent() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
// 使用 useCallback 缓存函数
const handleClick1 = useCallback(() => {
setCount1(c => c + 1);
}, []);
const handleClick2 = useCallback(() => {
setCount2(c => c + 1);
}, []);
return (
Count1: {count1}, Count2: {count2}
);
}
**何时使用 useCallback**:
* 函数作为 props 传递给子组件,且子组件使用了 React.memo
* 函数作为 useEffect 的依赖项
* 函数被其他 Hooks(如 useMemo)依赖
* 函数是一个复杂计算或创建成本较高
**避免过度使用**:
// 不需要 useCallback 的情况:简单函数直接内联
function SimpleComponent() {
const [count, setCount] = useState(0);
// 简单函数,不需要 useCallback
const increment = () => setCount(c => c + 1);
return ;
}
===== 5.17 useMemo - 缓存计算结果 =====
useMemo 返回一个 memoized 值。只有当依赖项发生变化时,才会重新计算。
**基本用法**:
import { useMemo } from 'react';
function ExpensiveComponent({ data, filter }) {
// 使用 useMemo 缓存昂贵的计算结果
const filteredData = useMemo(() => {
console.log('计算过滤数据');
return data.filter(item => item.name.includes(filter));
}, [data, filter]);
return (
{filteredData.map(item => - {item.name}
)}
);
}
**对象/数组的稳定性**:
function Chart({ data, options }) {
// 使用 useMemo 保持对象的引用稳定
const chartOptions = useMemo(() => ({
responsive: true,
scales: {
y: { beginAtZero: options.startFromZero }
}
}), [options.startFromZero]);
// chartOptions 的引用只在依赖变化时改变
useEffect(() => {
console.log('options 变化,重新初始化图表');
initChart(chartOptions);
}, [chartOptions]);
return ;
}
**复杂数据的处理**:
function DataTable({ rows, sortKey, sortOrder }) {
const sortedRows = useMemo(() => {
return [...rows].sort((a, b) => {
const aVal = a[sortKey];
const bVal = b[sortKey];
if (sortOrder === 'asc') {
return aVal > bVal ? 1 : -1;
} else {
return aVal < bVal ? 1 : -1;
}
});
}, [rows, sortKey, sortOrder]);
return (
{sortedRows.map(row => (
{row.name}
{row.value}
))}
);
}
**useMemo vs useCallback**:
// useCallback(fn, deps) 等同于 useMemo(() => fn, deps)
// useCallback - 缓存函数
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
// useMemo - 缓存值
const memoizedValue = useMemo(() => {
return computeExpensiveValue(a, b);
}, [a, b]);
===== 5.18 useContext - 跨组件状态共享 =====
useContext 让我们无需为每层组件手动添加 props,就能在组件树间进行数据传递。
**创建和使用 Context**:
import { createContext, useContext, useState } from 'react';
// 1. 创建 Context
const ThemeContext = createContext('light');
// 2. 提供 Context
function App() {
const [theme, setTheme] = useState('light');
return (
);
}
// 3. 消费 Context
function Toolbar() {
return (
);
}
function ThemeButton() {
// 使用 useContext 获取上下文值
const { theme, setTheme } = useContext(ThemeContext);
return (
);
}
**多 Context 使用**:
const ThemeContext = createContext('light');
const UserContext = createContext(null);
function App() {
const [theme, setTheme] = useState('light');
const [user, setUser] = useState({ name: '张三' });
return (
);
}
function Layout() {
const { theme } = useContext(ThemeContext);
const { user } = useContext(UserContext);
return (
欢迎, {user.name}
);
}
**Context 分割优化**:
// 不好的做法:整个 context 变化导致所有消费者重新渲染
const AppContext = createContext({
theme: 'light',
user: null,
setTheme: () => {},
setUser: () => {}
});
// 更好的做法:将经常变化和很少变化的数据分开
const ThemeContext = createContext({ theme: 'light', setTheme: () => {} });
const UserContext = createContext({ user: null, setUser: () => {} });
// 最佳做法:使用自定义 Hook 封装 context
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme 必须在 ThemeProvider 内部使用');
}
return context;
}
===== 5.19 useReducer - 复杂状态管理 =====
useReducer 是 useState 的替代方案,适用于 state 逻辑较复杂或包含多个子值的情况。
**基本用法**:
import { useReducer } from 'react';
// 定义 reducer 函数
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:
throw new Error('未知 action');
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
Count: {state.count}
);
}
**复杂状态管理**:
const initialState = {
loading: false,
data: null,
error: null
};
function dataReducer(state, action) {
switch (action.type) {
case 'FETCH_START':
return { ...state, loading: true, error: null };
case 'FETCH_SUCCESS':
return { ...state, loading: false, data: action.payload };
case 'FETCH_ERROR':
return { ...state, loading: false, error: action.payload };
default:
return state;
}
}
function DataComponent() {
const [state, dispatch] = useReducer(dataReducer, initialState);
const fetchData = async () => {
dispatch({ type: 'FETCH_START' });
try {
const response = await fetch('/api/data');
const data = await response.json();
dispatch({ type: 'FETCH_SUCCESS', payload: data });
} catch (error) {
dispatch({ type: 'FETCH_ERROR', payload: error.message });
}
};
return (
{state.loading && 加载中...
}
{state.error && 错误: {state.error}
}
{state.data && 数据: {JSON.stringify(state.data)}
}
);
}
**useReducer vs useState**:
| 场景 | 推荐方案 |
| 简单状态(单个值) | useState |
| 复杂状态(多个子值相互依赖) | useReducer |
| 需要优化性能,state 更新逻辑复杂 | useReducer |
| 需要复用状态逻辑 | useReducer + 自定义 Hook |
===== 5.20 其他常用 Hooks =====
**useId - 生成唯一 ID**:
import { useId } from 'react';
function Form() {
const id = useId();
return (
);
}
**useTransition - 非紧急更新**:
import { useTransition, useState } from 'react';
function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('home');
const selectTab = (nextTab) => {
// 将状态更新标记为低优先级
startTransition(() => {
setTab(nextTab);
});
};
return (
{isPending && 加载中...
}
selectTab('home')}>首页
selectTab('about')}>关于
{tab === 'home' && }
{tab === 'about' && }
);
}
**useDeferredValue - 延迟更新值**:
import { useDeferredValue, useState } from 'react';
function SearchResults() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
setQuery(e.target.value)}
placeholder="搜索..."
/>
{/* 使用 deferredQuery 显示结果,保持输入流畅 */}
);
}
===== 5.21 自定义 Hooks =====
自定义 Hooks 是提取组件逻辑到可复用函数的方式。自定义 Hook 是一个函数,其名称以 "use" 开头,内部可以调用其他 Hooks。
**useFetch - 数据获取**:
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
async function fetchData() {
try {
setLoading(true);
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) {
throw new Error(`HTTP 错误: ${response.status}`);
}
const result = await response.json();
setData(result);
setError(null);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err.message);
setData(null);
}
} finally {
setLoading(false);
}
}
fetchData();
return () => controller.abort();
}, [url]);
return { data, loading, error };
}
// 使用
function UserList() {
const { data: users, loading, error } = useFetch('/api/users');
if (loading) return 加载中...;
if (error) return 错误: {error};
return (
{users.map(user => - {user.name}
)}
);
}
**useLocalStorage - 本地存储同步**:
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// 获取初始值
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
// 当值变化时更新 localStorage
useEffect(() => {
try {
window.localStorage.setItem(key, JSON.stringify(storedValue));
} catch (error) {
console.error(error);
}
}, [key, storedValue]);
return [storedValue, setStoredValue];
}
// 使用
function App() {
const [name, setName] = useLocalStorage('name', '');
return (
setName(e.target.value)}
placeholder="输入你的名字"
/>
);
}
**useDebounce - 防抖**:
import { useState, useEffect } from 'react';
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(timer);
};
}, [value, delay]);
return debouncedValue;
}
// 使用
function SearchInput() {
const [input, setInput] = useState('');
const debouncedInput = useDebounce(input, 500);
useEffect(() => {
if (debouncedInput) {
performSearch(debouncedInput);
}
}, [debouncedInput]);
return (
setInput(e.target.value)}
placeholder="搜索..."
/>
);
}
**usePrevious - 获取上一次的值**:
import { useRef, useEffect } from 'react';
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
// 使用
function Counter() {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
return (
当前: {count}, 上一次: {prevCount}
);
}
**useOnClickOutside - 点击外部关闭**:
import { useEffect, useRef } from 'react';
function useOnClickOutside(ref, handler) {
useEffect(() => {
const listener = (event) => {
// 如果点击的是 ref 元素内部,不执行 handler
if (!ref.current || ref.current.contains(event.target)) {
return;
}
handler(event);
};
document.addEventListener('mousedown', listener);
document.addEventListener('touchstart', listener);
return () => {
document.removeEventListener('mousedown', listener);
document.removeEventListener('touchstart', listener);
};
}, [ref, handler]);
}
// 使用
function Dropdown() {
const ref = useRef();
const [isOpen, setIsOpen] = useState(false);
useOnClickOutside(ref, () => setIsOpen(false));
return (
{isOpen && 下拉内容}
);
}
===== 5.22 Hooks 最佳实践 =====
**1. 只在最顶层调用 Hooks**:
// 正确
function Example() {
const [count, setCount] = useState(0);
useEffect(() => { });
if (condition) { // 条件在 Hooks 之后
return;
}
}
// 错误 - 条件中使用 Hook
function Example() {
if (condition) {
const [count, setCount] = useState(0); // 错误!
}
}
**2. 只在 React 函数中调用 Hooks**:
// 正确 - 在函数组件中
function MyComponent() {
const [state, setState] = useState(0);
}
// 正确 - 在自定义 Hook 中
function useMyHook() {
const [state, setState] = useState(0);
}
**3. 使用 ESLint 插件**:
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
**4. 分离不相关的副作用**:
// 好的做法:分离关注点
function Example() {
// 处理订阅
useEffect(() => {
const subscription = subscribe();
return () => subscription.unsubscribe();
}, []);
// 处理 DOM 操作
useEffect(() => {
document.title = '新标题';
}, []);
}
**5. 避免过度优化**:
// 不需要 useMemo 的简单计算
const doubled = count * 2;
// 不需要 useCallback 的简单函数
const handleClick = () => setCount(c => c + 1);
===== 5.23 常见陷阱与解决方案 =====
**陷阱 1:useEffect 无限循环**
// 错误:setState 导致重新渲染,形成无限循环
useEffect(() => {
setCount(count + 1);
}, [count]);
// 正确:使用函数式更新
useEffect(() => {
const timer = setTimeout(() => {
setCount(c => c + 1);
}, 1000);
return () => clearTimeout(timer);
}, []);
**陷阱 2:遗漏依赖**
// 错误:遗漏了 count 依赖
useEffect(() => {
console.log(`当前计数: ${count}`);
}, []); // 警告:count 应该在依赖数组中
// 正确:添加所有依赖
useEffect(() => {
console.log(`当前计数: ${count}`);
}, [count]);
**陷阱 3:对象和数组依赖**
// 问题:options 每次都是新对象,导致 effect 每次都执行
useEffect(() => {
fetchData(options);
}, [options]);
// 方案1:展开为原始值
useEffect(() => {
fetchData({ page, size });
}, [page, size]);
// 方案2:使用 useMemo
const options = useMemo(() => ({ page, size }), [page, size]);
===== 5.24 总结 =====
本章全面介绍了 React 组件和 Hooks:
**组件基础**:
* 函数组件和类组件的定义与区别
* Props 传递、默认值和类型检查
* 组件组合、children 和 render props
* 高阶组件和组件组织方式
**核心 Hooks**:
* useState:状态管理
* useEffect:副作用处理
* useRef:DOM 引用和持久化值
* useCallback:缓存回调函数
* useMemo:缓存计算结果
* useContext:跨组件状态共享
* useReducer:复杂状态管理
**其他 Hooks**:
* useId:生成唯一 ID
* useTransition:非紧急更新
* useDeferredValue:延迟更新值
**自定义 Hooks**:
* 封装可复用逻辑
* 常见的自定义 Hooks 模式
**最佳实践**:
* Hooks 使用规则
* 性能优化策略
* 常见陷阱与解决方案
掌握这些知识后,你可以使用函数组件和 Hooks 构建高质量的 React 应用。记住 Hooks 的核心原则:只在最顶层调用、只在 React 函数中调用、保持依赖数组完整。