Skip to Content

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 规则

重要规则

  1. 必须有一个根元素
  2. 所有标签必须闭合
  3. 使用 camelCase 命名属性
  4. 使用大括号嵌入 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