React 常用操作笔记
一、React 概述
1.1 React 简介
React 是一个用于构建用户界面的 JavaScript 库,由 Facebook 开发和维护。
核心特点:
- 组件化:将 UI 拆分为独立、可复用的组件
- 声明式:描述 UI 应该是什么样子,而不是如何操作 DOM
- 虚拟 DOM:高效的 DOM 更新机制
- 单向数据流:数据从父组件流向子组件
- JSX 语法:在 JavaScript 中写类似 HTML 的代码
1.2 React 生态系统
- React DOM:渲染到浏览器
- React Native:构建移动应用
- React Router:路由管理
- Redux/ Zustand:状态管理
- Next.js:服务端渲染框架
二、JSX 语法
2.1 JSX 基础
jsx
// JSX 表达式
const element = <h1>Hello, World!</h1>;
// 嵌入表达式
const name = 'React';
const element = <h1>Hello, {name}!</h1>;
// 多行 JSX(使用括号)
const element = (
<div>
<h1>Title</h1>
<p>Content</p>
</div>
);2.2 JSX 属性
jsx
// 字符串属性
const element = <div className="container">Content</div>;
// 表达式属性
const element = <img src={user.avatarUrl} alt={user.name} />;
// 布尔属性
const element = <input disabled={true} />;
// 简写
const element = <input disabled />;
// 展开属性
const props = { name: 'React', version: '18' };
const element = <div {...props} />;2.3 JSX 注意事项
- JSX 必须有一个根元素(React 18+ 支持 Fragment)
- 属性名使用驼峰命名(如
className、onClick) - 所有标签必须闭合
- 使用
{}包裹 JavaScript 表达式
三、组件
3.1 函数组件
jsx
// 简单函数组件
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>;
}
// 箭头函数组件
const Welcome = (props) => {
return <h1>Hello, {props.name}!</h1>;
};
// 使用组件
function App() {
return (
<div>
<Welcome name="React" />
<Welcome name="World" />
</div>
);
}3.2 Props(属性)
jsx
// 传递 Props
function Avatar({ person, size }) {
return (
<img
src={person.imageUrl}
alt={person.name}
width={size}
height={size}
/>
);
}
// 使用 children prop
function Card({ children, title }) {
return (
<div className="card">
<h1>{title}</h1>
{children}
</div>
);
}
// 使用
<Card title="Photo">
<Avatar person={person} size={100} />
</Card>
// 展开属性传递
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}3.3 条件渲染
jsx
// if 语句
function Greeting({ isLoggedIn }) {
if (isLoggedIn) {
return <h1>Welcome back!</h1>;
}
return <h1>Please sign up.</h1>;
}
// 三元运算符
function Welcome({ user }) {
return (
<div>
{user ? <h1>Hello, {user.name}!</h1> : <h1>Please log in.</h1>}
</div>
);
}
// 逻辑与运算符
function Mailbox({ unreadMessages }) {
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 && (
<h2>You have {unreadMessages.length} unread messages.</h2>
)}
</div>
);
}3.4 列表渲染
jsx
// map 方法
function NumberList({ numbers }) {
const listItems = numbers.map((number) => (
<li key={number.toString()}>{number}</li>
));
return <ul>{listItems}</ul>;
}
// 直接使用
function TodoList({ todos }) {
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}注意事项:
- 每个列表项必须有唯一的
key属性 key应该是稳定的、可预测的、唯一的- 不要使用数组索引作为
key(除非列表不会改变)
四、State(状态)
4.1 useState Hook
jsx
import { useState } from 'react';
function Counter() {
// 声明 state 变量
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}4.2 State 更新
jsx
// 直接更新
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return <button onClick={handleClick}>{count}</button>;
}
// 函数式更新(推荐用于依赖前一个 state)
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(prevCount => prevCount + 1);
};
return <button onClick={handleClick}>{count}</button>;
}
// 批量更新
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
setCount(count + 1); // 只会执行一次更新
setCount(prevCount => prevCount + 1); // 正确的方式
setCount(prevCount => prevCount + 1);
};
return <button onClick={handleClick}>{count}</button>;
}4.3 对象 State
jsx
// 更新对象 state(不要直接修改)
function Form() {
const [formData, setFormData] = useState({
name: '',
email: ''
});
// ❌ 错误:直接修改
const handleChange = (e) => {
formData[e.target.name] = e.target.value;
setFormData(formData);
};
// ✅ 正确:创建新对象
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
return (
<form>
<input
name="name"
value={formData.name}
onChange={handleChange}
/>
<input
name="email"
value={formData.email}
onChange={handleChange}
/>
</form>
);
}4.4 数组 State
jsx
function TodoList() {
const [todos, setTodos] = useState([]);
// 添加
const addTodo = (text) => {
setTodos([...todos, { id: Date.now(), text }]);
};
// 删除
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
// 更新
const updateTodo = (id, newText) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, text: newText } : todo
));
};
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}五、事件处理
5.1 事件处理基础
jsx
function Button() {
// 函数声明
function handleClick() {
console.log('Button clicked');
}
return <button onClick={handleClick}>Click me</button>;
// 箭头函数
const handleClick = () => {
console.log('Button clicked');
};
return <button onClick={handleClick}>Click me</button>;
// 内联箭头函数
return <button onClick={() => console.log('Clicked')}>Click me</button>;
}5.2 传递参数
jsx
function TodoList({ todos, onDelete }) {
// 方式1:箭头函数
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
<button onClick={() => onDelete(todo.id)}>Delete</button>
</li>
))}
</ul>
);
// 方式2:bind
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
<button onClick={onDelete.bind(null, todo.id)}>Delete</button>
</li>
))}
</ul>
);
}5.3 事件对象
jsx
function Form() {
const handleSubmit = (e) => {
e.preventDefault(); // 阻止默认行为
console.log('Form submitted');
};
const handleChange = (e) => {
console.log('Value:', e.target.value);
};
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} />
<button type="submit">Submit</button>
</form>
);
}六、Effect Hook
6.1 useEffect 基础
jsx
import { useEffect } from 'react';
function MyComponent() {
// 每次渲染后执行
useEffect(() => {
// 副作用代码
console.log('Component rendered');
});
// 仅在挂载时执行一次
useEffect(() => {
console.log('Component mounted');
}, []);
// 依赖项改变时执行
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Count changed:', count);
}, [count]);
return <div>Component</div>;
}6.2 清理函数
jsx
function ChatRoom({ roomId }) {
useEffect(() => {
const connection = createConnection(roomId);
connection.connect();
// 清理函数
return () => {
connection.disconnect();
};
}, [roomId]);
return <h1>Welcome to room {roomId}!</h1>;
}6.3 常见使用场景
jsx
// 订阅事件
function App() {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
function handleMove(e) {
setPosition({ x: e.clientX, y: e.clientY });
}
window.addEventListener('pointermove', handleMove);
return () => window.removeEventListener('pointermove', handleMove);
}, []);
return <div>Position: {position.x}, {position.y}</div>;
}
// 数据获取
function Profile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
let ignore = false;
async function fetchUser() {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
if (!ignore) {
setUser(data);
}
}
fetchUser();
return () => {
ignore = true;
};
}, [userId]);
if (!user) return <div>Loading...</div>;
return <div>{user.name}</div>;
}七、其他常用 Hooks
7.1 useContext
jsx
import { createContext, useContext } from 'react';
// 创建 Context
const ThemeContext = createContext('light');
// 提供 Context
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
// 使用 Context
function Toolbar() {
const theme = useContext(ThemeContext);
return <div className={theme}>Toolbar</div>;
}7.2 useRef
jsx
import { useRef } from 'react';
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus Input</button>
</>
);
}
// 存储可变值(不触发重新渲染)
function Timer() {
const countRef = useRef(0);
const increment = () => {
countRef.current += 1;
console.log('Count:', countRef.current);
};
return <button onClick={increment}>Increment</button>;
}7.3 useMemo
jsx
import { useMemo } from 'react';
function ExpensiveComponent({ todos, filter }) {
// 缓存计算结果
const visibleTodos = useMemo(() => {
return todos.filter(todo => todo.status === filter);
}, [todos, filter]);
return (
<ul>
{visibleTodos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}7.4 useCallback
jsx
import { useCallback } from 'react';
function Parent() {
const [count, setCount] = useState(0);
// 缓存函数引用
const handleClick = useCallback(() => {
console.log('Clicked', count);
}, [count]);
return <Child onClick={handleClick} />;
}
function Child({ onClick }) {
return <button onClick={onClick}>Click me</button>;
}7.5 自定义 Hooks
jsx
// 自定义 Hook:数据获取
function useData(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let ignore = false;
async function fetchData() {
try {
setLoading(true);
const response = await fetch(url);
const json = await response.json();
if (!ignore) {
setData(json);
setError(null);
}
} catch (err) {
if (!ignore) {
setError(err);
}
} finally {
if (!ignore) {
setLoading(false);
}
}
}
fetchData();
return () => {
ignore = true;
};
}, [url]);
return { data, loading, error };
}
// 使用自定义 Hook
function Profile({ userId }) {
const { data, loading, error } = useData(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{data.name}</div>;
}
// 自定义 Hook:聊天室
function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
connection.on('message', onReceiveMessage);
return () => {
connection.disconnect();
};
}, [serverUrl, roomId, onReceiveMessage]);
}
// 使用
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useChatRoom({
serverUrl,
roomId,
onReceiveMessage(msg) {
showNotification('New message: ' + msg);
}
});
return <h1>Welcome to room {roomId}!</h1>;
}八、表单处理
8.1 受控组件
jsx
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
console.log('Name:', name, 'Email:', email);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<button type="submit">Submit</button>
</form>
);
}8.2 表单状态管理
jsx
function Form() {
const [formData, setFormData] = useState({
name: '',
email: '',
message: ''
});
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(formData);
};
return (
<form onSubmit={handleSubmit}>
<input
name="name"
value={formData.name}
onChange={handleChange}
/>
<input
name="email"
value={formData.email}
onChange={handleChange}
/>
<textarea
name="message"
value={formData.message}
onChange={handleChange}
/>
<button type="submit">Submit</button>
</form>
);
}8.3 表单验证
jsx
function Form() {
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
const [status, setStatus] = useState('typing');
const handleSubmit = async (e) => {
e.preventDefault();
setStatus('submitting');
setError(null);
try {
await submitForm(answer);
setStatus('success');
} catch (err) {
setStatus('typing');
setError(err);
}
};
if (status === 'success') {
return <h1>That's right!</h1>;
}
return (
<form onSubmit={handleSubmit}>
<textarea
value={answer}
onChange={(e) => setAnswer(e.target.value)}
disabled={status === 'submitting'}
/>
<button disabled={answer.length === 0 || status === 'submitting'}>
Submit
</button>
{error && <p className="error">{error.message}</p>}
</form>
);
}九、Context API
9.1 创建和使用 Context
jsx
import { createContext, useContext, useState } from 'react';
// 创建 Context
const ThemeContext = createContext();
// Provider 组件
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// 自定义 Hook
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// 使用
function App() {
return (
<ThemeProvider>
<Header />
<Content />
</ThemeProvider>
);
}
function Header() {
const { theme, toggleTheme } = useTheme();
return (
<header className={theme}>
<button onClick={toggleTheme}>Toggle Theme</button>
</header>
);
}十、性能优化
10.1 React.memo
jsx
import { memo } from 'react';
// 使用 memo 包装组件(浅比较 props)
const Child = memo(function Child({ name }) {
return <div>{name}</div>;
});
// 自定义比较函数
const Child = memo(
function Child({ user }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
return prevProps.user.id === nextProps.user.id;
}
);10.2 useMemo 优化计算
jsx
function ExpensiveComponent({ todos, filter }) {
// 只有 todos 或 filter 改变时才重新计算
const filteredTodos = useMemo(() => {
return todos.filter(todo => todo.status === filter);
}, [todos, filter]);
return (
<ul>
{filteredTodos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}10.3 useCallback 优化函数
jsx
function Parent() {
const [count, setCount] = useState(0);
// 缓存函数引用,避免子组件不必要的重新渲染
const handleClick = useCallback(() => {
console.log('Clicked');
}, []);
return <Child onClick={handleClick} />;
}
const Child = memo(function Child({ onClick }) {
return <button onClick={onClick}>Click me</button>;
});10.4 代码分割
jsx
import { lazy, Suspense } from 'react';
// 懒加载组件
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}十一、React 常用插件整理
11.1 路由管理
React Router
bash
npm install react-router-domjsx
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
);
}功能:
- 声明式路由
- 嵌套路由
- 路由守卫
- 代码分割
- 服务端渲染支持
11.2 状态管理
Redux Toolkit
bash
npm install @reduxjs/toolkit react-redux功能:
- 全局状态管理
- 中间件支持(如 Redux Thunk、Redux Saga)
- 时间旅行调试
- 适用于大型应用
Zustand
bash
npm install zustandjsx
import create from 'zustand';
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}));
function Counter() {
const { count, increment } = useStore();
return <button onClick={increment}>{count}</button>;
}功能:
- 轻量级状态管理
- 无需 Provider
- 简单的 API
- 适用于中小型应用
Jotai / Recoil
- Jotai:基于原子化状态管理
- Recoil:Facebook 官方状态管理库
11.3 UI 组件库
Ant Design
bash
npm install antd功能:
- 丰富的组件库
- 企业级 UI 设计
- TypeScript 支持
- 主题定制
Material-UI (MUI)
bash
npm install @mui/material @emotion/react @emotion/styled功能:
- Google Material Design
- 完整的组件库
- 主题系统
- 响应式设计
Chakra UI
bash
npm install @chakra-ui/react @emotion/react @emotion/styled framer-motion功能:
- 简洁的 API
- 内置主题系统
- 可访问性支持
- 响应式设计
11.4 表单处理
React Hook Form
bash
npm install react-hook-formjsx
import { useForm } from 'react-hook-form';
function Form() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('name', { required: true })} />
{errors.name && <span>This field is required</span>}
<button type="submit">Submit</button>
</form>
);
}功能:
- 高性能表单处理
- 内置验证
- 减少重新渲染
- 易于使用
Formik
bash
npm install formik功能:
- 完整的表单解决方案
- 验证支持
- 错误处理
11.5 数据获取
React Query (TanStack Query)
bash
npm install @tanstack/react-queryjsx
import { useQuery } from '@tanstack/react-query';
function Profile({ userId }) {
const { data, isLoading, error } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetch(`/api/users/${userId}`).then(res => res.json())
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{data.name}</div>;
}功能:
- 数据获取和缓存
- 自动后台更新
- 乐观更新
- 请求去重
SWR
bash
npm install swr功能:
- 数据获取库
- 自动重新验证
- 请求去重
- 本地缓存
11.6 样式方案
Styled-components
bash
npm install styled-componentsjsx
import styled from 'styled-components';
const Button = styled.button`
background: ${props => props.primary ? 'blue' : 'white'};
color: ${props => props.primary ? 'white' : 'blue'};
padding: 10px 20px;
border: none;
border-radius: 4px;
`;
function App() {
return (
<>
<Button>Normal</Button>
<Button primary>Primary</Button>
</>
);
}功能:
- CSS-in-JS
- 样式组件化
- 主题支持
- 动态样式
Tailwind CSS
bash
npm install -D tailwindcss postcss autoprefixer功能:
- 实用优先的 CSS 框架
- 响应式设计
- 自定义主题
- 快速开发
CSS Modules
jsx
import styles from './Button.module.css';
function Button() {
return <button className={styles.button}>Click me</button>;
}11.7 动画库
Framer Motion
bash
npm install framer-motionjsx
import { motion } from 'framer-motion';
function App() {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
>
Animated content
</motion.div>
);
}功能:
- 强大的动画库
- 手势支持
- 布局动画
- 易于使用
React Spring
bash
npm install react-spring功能:
- 基于物理的动画
- 性能优化
- 丰富的动画类型
11.8 工具库
React Helmet
bash
npm install react-helmet功能:
- 管理 document head
- SEO 优化
- 动态 meta 标签
React Icons
bash
npm install react-iconsjsx
import { FaReact, FaGithub } from 'react-icons/fa';
function App() {
return (
<div>
<FaReact />
<FaGithub />
</div>
);
}功能:
- 图标库集合
- 支持多种图标库
- 易于使用
clsx / classnames
bash
npm install clsxjsx
import clsx from 'clsx';
function Button({ primary, disabled }) {
return (
<button
className={clsx('btn', {
'btn-primary': primary,
'btn-disabled': disabled
})}
>
Click me
</button>
);
}功能:
- 条件类名拼接
- 简化 className 逻辑
11.9 开发工具
React DevTools
- 浏览器扩展
- 组件树查看
- Props 和 State 检查
- 性能分析
React Hot Loader / Fast Refresh
bash
npm install -D @pmmmwh/react-refresh-webpack-plugin react-refresh功能:
- 热更新
- 保持组件状态
- 快速开发反馈
11.10 测试库
React Testing Library
bash
npm install -D @testing-library/react @testing-library/jest-dom功能:
- 组件测试
- 用户行为测试
- 可访问性测试
Jest
bash
npm install -D jest @testing-library/jest-dom功能:
- JavaScript 测试框架
- 快照测试
- Mock 功能
11.11 国际化
React i18next
bash
npm install react-i18next i18next功能:
- 国际化支持
- 多语言切换
- 翻译管理
11.12 其他实用库
React Markdown
bash
npm install react-markdown功能:
- 渲染 Markdown
- 支持自定义组件
- 插件系统
React Window
bash
npm install react-window功能:
- 虚拟滚动
- 大列表优化
- 性能提升
React DnD
bash
npm install react-dnd react-dnd-html5-backend功能:
- 拖拽功能
- 复杂交互
- 拖放 API
十二、最佳实践
12.1 组件设计原则
- 单一职责:每个组件只做一件事
- 可复用性:设计通用的组件
- 组合优于继承:使用组合构建复杂 UI
- 受控组件:表单元素应该受控
- key 属性:列表渲染必须使用 key
12.2 性能优化建议
- 避免不必要的重新渲染:使用
React.memo、useMemo、useCallback - 代码分割:使用
lazy和Suspense - 虚拟滚动:处理大列表时使用
react-window - 图片优化:使用懒加载和适当的格式
12.3 代码规范
- 组件命名:使用 PascalCase
- Hook 命名:使用
use前缀 - Props 类型:使用 TypeScript 或 PropTypes
- 代码组织:按功能组织文件
12.4 常见错误
- 直接修改 state:应该创建新对象/数组
- 忘记依赖项:useEffect 依赖项要完整
- 无限循环:避免在 useEffect 中触发状态更新导致循环
- key 不稳定:不要使用数组索引作为 key
学习资源:
推荐学习路径:
- 掌握 JSX 语法和组件基础
- 理解 State 和 Props
- 学习 Hooks(useState、useEffect 等)
- 掌握事件处理和表单
- 了解 Context API
- 学习性能优化技巧
- 熟悉常用插件和工具库
- 实践项目开发
