3.3 React组件与JSX
React 是现代前端开发的核心技术,理解组件化思想和 JSX 语法对于 AI 编程至关重要。
核心理念:React 让你把 UI 拆分成独立、可复用的组件,每个组件管理自己的状态和逻辑。
React 组件基础
什么是 React 组件?
React 组件是构建用户界面的独立、可复用的代码块。就像乐高积木一样,你可以用组件搭建复杂的界面。
函数组件 vs 类组件
函数组件(推荐)
// 简单的函数组件
function Welcome() {
return <h1>欢迎来到我的网站!</h1>;
}
// 带参数的函数组件
function Welcome({ name }: { name: string }) {
return <h1>欢迎,{name}!</h1>;
}
// 使用箭头函数
const Welcome = ({ name }: { name: string }) => {
return <h1>欢迎,{name}!</h1>;
};
// 导出组件
export default Welcome;优势:
- 语法简洁
- 支持 Hooks
- 更好的性能
- 更容易测试
类组件(传统)
import React from 'react';
class Welcome extends React.Component {
render() {
return <h1>欢迎,{this.props.name}!</h1>;
}
}
// 带状态和生命周期的类组件
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>计数:{this.state.count}</p>
<button onClick={this.increment}>增加</button>
</div>
);
}
}劣势:
- 语法复杂
- this 指向问题
- 性能相对较差
JSX 语法
什么是 JSX?
JSX 是 JavaScript 的语法扩展,让你在 JavaScript 中编写类似 HTML 的代码。
JSX 基础语法
// JSX 基本语法
const element = <h1>Hello, world!</h1>;
// 表达式
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;
// 函数调用
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Harper',
lastName: 'Perez'
};
const element = <h1>Hello, {formatName(user)}!</h1>;
// 属性
const element = <div className="container">内容</div>;
const element = <img src={user.avatarUrl} alt={user.name} />;JSX 规则
重要规则:
- 必须有一个根元素
- 所有标签必须闭合
- 使用 camelCase 命名属性
- 使用大括号嵌入 JavaScript 表达式
// ✅ 正确的 JSX
function App() {
return (
<div>
<h1>标题</h1>
<p>段落内容</p>
</div>
);
}
// ✅ 使用 Fragment 避免额外 div
function App() {
return (
<>
<h1>标题</h1>
<p>段落内容</p>
</>
);
}
// ❌ 错误的 JSX - 多个根元素
function App() {
return (
<h1>标题</h1>
<p>段落内容</p> // 错误:多个根元素
);
}Props 和 State
Props(属性)
Props 是组件的输入,类似于函数的参数。
// 定义组件接收 props
interface CardProps {
title: string;
content: string;
author?: string; // 可选属性
onLike?: () => void; // 函数属性
}
function Card({ title, content, author, onLike }: CardProps) {
return (
<div className="card">
<h2>{title}</h2>
<p>{content}</p>
{author && <p>作者:{author}</p>}
{onLike && <button onClick={onLike}>点赞</button>}
</div>
);
}
// 使用组件并传递 props
function App() {
return (
<Card
title="React 组件"
content="这是关于 React 组件的介绍..."
author="张三"
onLike={() => console.log('已点赞')}
/>
);
}State(状态)
State 是组件内部的数据,可以随时间变化。
import { useState } from 'react';
function Counter() {
// useState Hook 返回 [状态值, 更新函数]
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
return (
<div>
<p>当前计数:{count}</p>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
</div>
);
}
// 复杂状态
function UserProfile() {
const [user, setUser] = useState({
name: '',
email: '',
age: 0
});
const updateUser = (field: string, value: string | number) => {
setUser(prev => ({
...prev,
[field]: value
}));
};
return (
<div>
<input
type="text"
placeholder="姓名"
value={user.name}
onChange={(e) => updateUser('name', e.target.value)}
/>
<input
type="email"
placeholder="邮箱"
value={user.email}
onChange={(e) => updateUser('email', e.target.value)}
/>
<input
type="number"
placeholder="年龄"
value={user.age}
onChange={(e) => updateUser('age', parseInt(e.target.value))}
/>
</div>
);
}常用 React Hooks
useState
状态管理 Hook。
import { useState } from 'react';
// 多个状态
function TodoList() {
const [todos, setTodos] = useState([]);
const [inputValue, setInputValue] = useState('');
const [filter, setFilter] = useState('all');
const addTodo = () => {
if (inputValue.trim()) {
setTodos([...todos, {
id: Date.now(),
text: inputValue,
completed: false
}]);
setInputValue('');
}
};
return (
<div>
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="添加待办事项"
/>
<button onClick={addTodo}>添加</button>
<select value={filter} onChange={(e) => setFilter(e.target.value)}>
<option value="all">全部</option>
<option value="active">未完成</option>
<option value="completed">已完成</option>
</select>
</div>
);
}useEffect
副作用处理 Hook。
import { useState, useEffect } from 'react';
// 数据获取
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchUsers() {
try {
const response = await fetch('/api/users');
const data = await response.json();
setUsers(data);
} catch (error) {
console.error('获取用户失败:', error);
} finally {
setLoading(false);
}
}
fetchUsers();
}, []); // 空数组表示只在组件挂载时执行
if (loading) return <div>加载中...</div>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// 事件监听
function WindowSize() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight
});
}
window.addEventListener('resize', handleResize);
// 清理函数
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // 空数组,effect 只运行一次
return (
<div>
窗口大小:{windowSize.width} x {windowSize.height}
</div>
);
}useContext
跨组件共享状态。
import { createContext, useContext, useState } from 'react';
// 创建 Context
const ThemeContext = createContext<{
theme: 'light' | 'dark';
toggleTheme: () => void;
}>({
theme: 'light',
toggleTheme: () => {}
});
// 主题提供者组件
function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// 使用 Context 的 Hook
function useTheme() {
return useContext(ThemeContext);
}
// 在组件中使用
function Header() {
const { theme, toggleTheme } = useTheme();
return (
<header className={theme}>
<h1>我的应用</h1>
<button onClick={toggleTheme}>
切换到{theme === 'light' ? '深色' : '浅色'}主题
</button>
</header>
);
}自定义 Hook
复用组件逻辑。
// 自定义 Hook:获取窗口大小
function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: 0,
height: 0,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
handleResize();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return windowSize;
}
// 自定义 Hook:本地存储
function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error('保存到本地存储失败:', error);
}
};
return [storedValue, setValue] as const;
}
// 使用自定义 Hook
function App() {
const windowSize = useWindowSize();
const [name, setName] = useLocalStorage('name', '');
return (
<div>
<p>窗口大小:{windowSize.width} x {windowSize.height}</p>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="输入你的名字"
/>
<p>保存的名字:{name}</p>
</div>
);
}事件处理
事件绑定
function ButtonExample() {
const handleClick = () => {
console.log('按钮被点击了');
};
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault(); // 阻止默认行为
console.log('表单提交');
};
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
console.log('输入值:', event.target.value);
};
return (
<div>
{/* 基本点击事件 */}
<button onClick={handleClick}>点击我</button>
{/* 内联事件处理 */}
<button onClick={() => alert('内联处理')}>
内联处理
</button>
{/* 表单事件 */}
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={handleInputChange}
placeholder="输入文本"
/>
<button type="submit">提交</button>
</form>
{/* 其他事件 */}
<div
onMouseEnter={() => console.log('鼠标进入')}
onMouseLeave={() => console.log('鼠标离开')}
>
鼠标悬停区域
</div>
</div>
);
}条件渲染
function ConditionalRendering() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [userRole, setUserRole] = useState<'admin' | 'user' | null>(null);
// 三元运算符
return (
<div>
{isLoggedIn ? (
<button onClick={() => setIsLoggedIn(false)}>
退出登录
</button>
) : (
<button onClick={() => setIsLoggedIn(true)}>
登录
</button>
)}
{/* 逻辑与运算符 */}
{isLoggedIn && userRole === 'admin' && (
<button>管理面板</button>
)}
{/* 多条件渲染 */}
{(() => {
if (!isLoggedIn) {
return <p>请先登录</p>;
}
if (userRole === 'admin') {
return <p>欢迎,管理员</p>;
}
return <p>欢迎,用户</p>;
})()}
</div>
);
}列表渲染
function ListRendering() {
const [items, setItems] = useState([
{ id: 1, name: '苹果', price: 5 },
{ id: 2, name: '香蕉', price: 3 },
{ id: 3, name: '橙子', price: 4 }
]);
const [filter, setFilter] = useState('');
// 过滤和渲染列表
const filteredItems = items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
const addItem = () => {
const newItem = {
id: Date.now(),
name: `新商品 ${items.length + 1}`,
price: Math.floor(Math.random() * 10) + 1
};
setItems([...items, newItem]);
};
return (
<div>
<input
type="text"
placeholder="搜索商品..."
value={filter}
onChange={(e) => setFilter(e.target.value)}
/>
<button onClick={addItem}>添加商品</button>
<ul>
{filteredItems.map(item => (
<li key={item.id}>
<span>{item.name}</span>
<span>¥{item.price}</span>
<button
onClick={() => setItems(items.filter(i => i.id !== item.id))}
>
删除
</button>
</li>
))}
</ul>
{filteredItems.length === 0 && (
<p>没有找到匹配的商品</p>
)}
</div>
);
}组件组合
组件嵌套
// 基础组件
function Button({ children, onClick, variant = 'primary' }: {
children: React.ReactNode;
onClick?: () => void;
variant?: 'primary' | 'secondary';
}) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{children}
</button>
);
}
// Card 组件
function Card({ title, content, footer }: {
title: string;
content: string;
footer?: React.ReactNode;
}) {
return (
<div className="card">
<h3>{title}</h3>
<p>{content}</p>
{footer && <div className="card-footer">{footer}</div>}
</div>
);
}
// 组合使用
function ProductCard({ product }: { product: { id: number; name: string; price: number } }) {
return (
<Card
title={product.name}
content={`价格:¥${product.price}`}
footer={
<div>
<Button variant="secondary">查看详情</Button>
<Button onClick={() => console.log('添加到购物车')}>
加入购物车
</Button>
</div>
}
/>
);
}Children 属性
// 容器组件
function Container({ children, title }: {
children: React.ReactNode;
title: string;
}) {
return (
<div className="container">
<h2>{title}</h2>
<div className="content">
{children}
</div>
</div>
);
}
// 使用
function App() {
return (
<Container title="用户列表">
<p>这里是可以放任意内容的区域</p>
<ul>
<li>用户1</li>
<li>用户2</li>
</ul>
<Button>添加用户</Button>
</Container>
);
}AI 编程中的 React
AI 生成 React 组件的技巧:
你是一位 React 专家,请帮我创建一个任务管理组件:
功能要求:
1. 显示任务列表
2. 添加新任务
3. 标记任务完成状态
4. 删除任务
5. 任务数量统计
技术要求:
- 使用 TypeScript
- 函数组件 + Hooks
- 响应式设计
- 良好的用户体验
- 适当的错误处理
样式要求:
- 使用 Tailwind CSS
- 现代化设计风格
- 支持深色模式切换
- 添加过渡动画效果
请提供:
1. 完整的组件代码
2. 类型定义
3. 使用示例
4. 功能说明下一步:掌握了 React 基础后,继续学习 样式解决方案,了解现代化的 CSS 处理方案。
Last updated on