React 18.x 学习计划 - 第三天:React基础概念
学习目标
- 搭建React开发环境
- 创建第一个React组件
- 掌握JSX语法
- 理解组件和Props
- 学会事件处理
学习时间安排
总时长:5-6小时
- 环境搭建:1小时
- React基础:2小时
- JSX语法:1.5小时
- 实践项目:1.5-2小时
第一部分:React环境搭建 (1小时)
1.1 安装Node.js和npm
检查环境
# 检查Node.js版本
node --version# 检查npm版本
npm --version# 如果版本过低,需要升级
# Node.js 推荐版本:16.x 或更高
# npm 推荐版本:8.x 或更高
升级npm(如果需要)
# 升级npm到最新版本
npm install -g npm@latest# 验证升级结果
npm --version
1.2 创建React项目
使用Create React App
# 创建新的React项目
npx create-react-app my-first-react-app# 进入项目目录
cd my-first-react-app# 启动开发服务器
npm start
项目结构说明
my-first-react-app/
├── public/
│ ├── index.html # HTML模板
│ ├── favicon.ico # 网站图标
│ └── manifest.json # PWA配置
├── src/
│ ├── App.js # 主应用组件
│ ├── App.css # 应用样式
│ ├── index.js # 应用入口
│ ├── index.css # 全局样式
│ └── logo.svg # React logo
├── package.json # 项目配置
└── README.md # 项目说明
1.3 开发工具配置
VS Code扩展推荐
// .vscode/extensions.json
{"recommendations": ["ms-vscode.vscode-typescript-next","bradlc.vscode-tailwindcss","esbenp.prettier-vscode","ms-vscode.vscode-eslint"]
}
代码格式化配置
// .vscode/settings.json
{"editor.formatOnSave": true,"editor.defaultFormatter": "esbenp.prettier-vscode","editor.codeActionsOnSave": {"source.fixAll.eslint": true}
}
Prettier配置
// .prettierrc
{"semi": true,"trailingComma": "es5","singleQuote": true,"printWidth": 80,"tabWidth": 2
}
第二部分:React基础概念 (2小时)
2.1 第一个React组件
函数组件(详细注释版)
// src/components/HelloWorld.js
// 导入React库,这是使用React组件的必要步骤
import React from 'react';// 定义一个函数组件
// 函数组件是React中最简单的组件类型
// 它接收props作为参数,返回JSX元素
function HelloWorld() {// 返回JSX元素// JSX是JavaScript的扩展语法,允许我们在JavaScript中写HTMLreturn (<div>{/* 这是一个注释,在JSX中使用花括号和斜杠 */}<h1>Hello, React!</h1><p>Welcome to React 18.x</p>{/* 可以在JSX中使用JavaScript表达式 */}<p>Current time: {new Date().toLocaleString()}</p></div>);
}// 导出组件,这样其他文件就可以导入并使用这个组件
export default HelloWorld;
类组件(详细注释版)
// src/components/HelloWorldClass.js
// 导入React和Component类
import React, { Component } from 'react';// 定义一个类组件
// 类组件继承自React.Component
// 类组件有状态和生命周期方法
class HelloWorldClass extends Component {// render方法是类组件必须实现的方法// 它返回要渲染的JSX元素render() {return (<div><h1>Hello, React!</h1><p>This is a class component</p>{/* 在类组件中,可以通过this.props访问传入的属性 */}<p>Props received: {JSON.stringify(this.props)}</p></div>);}
}// 导出组件
export default HelloWorldClass;
组件使用(详细注释版)
// src/App.js
// 导入React库
import React from 'react';
// 导入我们创建的组件
import HelloWorld from './components/HelloWorld';
import HelloWorldClass from './components/HelloWorldClass';
// 导入样式文件
import './App.css';// 定义主应用组件
// 这是整个应用的根组件
function App() {// 返回JSX元素return (<div className="App">{/* 应用头部 */}<header className="App-header">{/* 使用我们创建的组件 */}<HelloWorld /><HelloWorldClass /></header></div>);
}// 导出App组件,这样index.js就可以使用它
export default App;
2.2 组件属性(Props)详细讲解
基本Props使用(详细注释版)
// src/components/UserCard.js
// 导入React库
import React from 'react';// 定义UserCard组件
// 这个组件接收props参数,props是一个对象,包含父组件传递的所有属性
function UserCard(props) {// 从props中解构出需要的属性// 这样可以让代码更清晰,避免重复写props.const { name, age, email, location } = props;// 返回用户卡片JSXreturn (<div className="user-card">{/* 显示用户姓名 */}<h2>{name}</h2>{/* 显示用户年龄 */}<p>Age: {age}</p>{/* 显示用户邮箱 */}<p>Email: {email}</p>{/* 显示用户位置 */}<p>Location: {location}</p></div>);
}// 导出组件
export default UserCard;
解构Props(详细注释版)
// src/components/UserCardDestructured.js
// 导入React库
import React from 'react';// 定义UserCardDestructured组件
// 在函数参数中直接解构props,这是更简洁的写法
function UserCardDestructured({ name, age, email, location }) {// 返回用户卡片JSXreturn (<div className="user-card"><h2>{name}</h2><p>Age: {age}</p><p>Email: {email}</p><p>Location: {location}</p></div>);
}// 导出组件
export default UserCardDestructured;
默认Props(详细注释版)
// src/components/UserCardWithDefaults.js
// 导入React库
import React from 'react';// 定义UserCardWithDefaults组件
// 在函数参数中设置默认值,当父组件没有传递某个属性时,会使用默认值
function UserCardWithDefaults({ name = 'Anonymous', // 默认姓名age = 0, // 默认年龄email = 'no-email@example.com', // 默认邮箱location = 'Unknown' // 默认位置
}) {// 返回用户卡片JSXreturn (<div className="user-card"><h2>{name}</h2><p>Age: {age}</p><p>Email: {email}</p><p>Location: {location}</p></div>);
}// 导出组件
export default UserCardWithDefaults;
使用组件(详细注释版)
// src/App.js
// 导入React库
import React from 'react';
// 导入我们创建的组件
import UserCard from './components/UserCard';
import UserCardDestructured from './components/UserCardDestructured';
import UserCardWithDefaults from './components/UserCardWithDefaults';
// 导入样式文件
import './App.css';// 定义主应用组件
function App() {// 定义用户数据对象// 这些数据将作为props传递给子组件const user1 = {name: 'John Doe',age: 30,email: 'john@example.com',location: 'New York'};const user2 = {name: 'Jane Smith',age: 25,email: 'jane@example.com',location: 'San Francisco'};// 返回应用JSXreturn (<div className="App"><h1>User Management</h1>{/* 使用UserCard组件,传递所有属性 */}<UserCard name={user1.name}age={user1.age}email={user1.email}location={user1.location}/>{/* 使用UserCardDestructured组件,使用展开运算符传递所有属性 */}<UserCardDestructured {...user2} />{/* 使用UserCardWithDefaults组件,不传递任何属性,使用默认值 */}<UserCardWithDefaults /></div>);
}// 导出App组件
export default App;
2.3 组件状态(State)详细讲解
函数组件使用useState(详细注释版)
// src/components/Counter.js
// 导入React和useState Hook
import React, { useState } from 'react';// 定义Counter组件
function Counter() {// 使用useState Hook来管理状态// useState返回一个数组,第一个元素是当前状态值,第二个元素是更新状态的函数// 这里我们初始化count为0const [count, setCount] = useState(0);// 定义增加计数器的函数const increment = () => {// 使用setCount函数来更新状态// 这里我们传递一个新的值setCount(count + 1);};// 定义减少计数器的函数const decrement = () => {// 更新状态,减少计数setCount(count - 1);};// 定义重置计数器的函数const reset = () => {// 重置计数为0setCount(0);};// 返回计数器JSXreturn (<div className="counter">{/* 显示当前计数 */}<h2>Counter: {count}</h2>{/* 增加按钮,点击时调用increment函数 */}<button onClick={increment}>+</button>{/* 减少按钮,点击时调用decrement函数 */}<button onClick={decrement}>-</button>{/* 重置按钮,点击时调用reset函数 */}<button onClick={reset}>Reset</button></div>);
}// 导出Counter组件
export default Counter;
类组件使用state(详细注释版)
// src/components/CounterClass.js
// 导入React和Component类
import React, { Component } from 'react';// 定义CounterClass类组件
class CounterClass extends Component {// 构造函数,用于初始化组件constructor(props) {// 调用父类构造函数super(props);// 初始化state对象// state是组件的内部状态this.state = {count: 0 // 初始计数为0};}// 定义增加计数器的箭头函数// 使用箭头函数可以确保this指向当前组件实例increment = () => {// 使用setState方法更新状态// setState是异步的,React会批量更新状态以提高性能this.setState({ count: this.state.count + 1 });};// 定义减少计数器的箭头函数decrement = () => {// 更新状态,减少计数this.setState({ count: this.state.count - 1 });};// 定义重置计数器的箭头函数reset = () => {// 重置计数为0this.setState({ count: 0 });};// render方法,返回要渲染的JSXrender() {return (<div className="counter">{/* 显示当前计数,从state中获取 */}<h2>Counter: {this.state.count}</h2>{/* 增加按钮 */}<button onClick={this.increment}>+</button>{/* 减少按钮 */}<button onClick={this.decrement}>-</button>{/* 重置按钮 */}<button onClick={this.reset}>Reset</button></div>);}
}// 导出CounterClass组件
export default CounterClass;
第三部分:JSX语法深入 (1.5小时)
3.1 JSX基础语法(详细注释版)
基本元素(详细注释版)
// src/components/JSXBasics.js
// 导入React库
import React from 'react';// 定义JSXBasics组件
function JSXBasics() {// 定义一些变量,这些变量将在JSX中使用const name = 'React';const version = '18.x';const isLearning = true;// 返回JSX元素return (<div><h1>JSX Basics</h1>{/* 这是JSX中的注释,使用花括号和斜杠 */}{/* 基本文本内容 */}<p>Hello, {name}!</p>{/* 表达式计算 */}<p>Version: {version}</p><p>2 + 2 = {2 + 2}</p>{/* 条件渲染 - 使用逻辑与运算符 */}{/* 只有当isLearning为true时,才会渲染后面的元素 */}{isLearning && <p>You are learning React!</p>}{/* 三元运算符 - 根据条件渲染不同内容 */}<p>Status: {isLearning ? 'Learning' : 'Not Learning'}</p>{/* 数组渲染 - 使用map方法遍历数组 */}<ul>{['HTML', 'CSS', 'JavaScript', 'React'].map((tech, index) => (<li key={index}>{tech}</li>))}</ul></div>);
}// 导出JSXBasics组件
export default JSXBasics;
属性设置(详细注释版)
// src/components/JSXAttributes.js
// 导入React库
import React from 'react';// 定义JSXAttributes组件
function JSXAttributes() {// 定义一些变量,这些变量将作为属性值const imageUrl = 'https://via.placeholder.com/300x200';const altText = 'Placeholder image';const className = 'custom-class';// 定义内联样式对象// 在React中,样式属性使用驼峰命名法const style = {backgroundColor: 'lightblue',padding: '20px',borderRadius: '8px'};// 返回JSX元素return (<div><h2>JSX Attributes</h2>{/* 基本属性 - src和alt */}<img src={imageUrl} alt={altText} />{/* className属性 - 用于CSS类名 */}<div className={className}>Styled div</div>{/* style属性 - 内联样式 */}<div style={style}>Inline styled div</div>{/* 布尔属性 - disabled和checked */}<input type="text" disabled={false} /><input type="checkbox" checked={true} />{/* 事件处理 - onClick事件 */}<button onClick={() => alert('Button clicked!')}>Click me</button></div>);
}// 导出JSXAttributes组件
export default JSXAttributes;
3.2 条件渲染(详细注释版)
条件渲染方法(详细注释版)
// src/components/ConditionalRendering.js
// 导入React和useState Hook
import React, { useState } from 'react';// 定义ConditionalRendering组件
function ConditionalRendering() {// 使用useState Hook管理状态const [isLoggedIn, setIsLoggedIn] = useState(false);const [userRole, setUserRole] = useState('user');const [showDetails, setShowDetails] = useState(false);// 方法1:三元运算符// 根据isLoggedIn的值渲染不同内容const renderLoginStatus = () => {return isLoggedIn ? (<p>Welcome back!</p>) : (<p>Please log in</p>);};// 方法2:逻辑与运算符// 只有当所有条件都为true时,才会渲染后面的元素const renderAdminPanel = () => {return isLoggedIn && userRole === 'admin' && (<div><h3>Admin Panel</h3><p>You have admin privileges</p></div>);};// 方法3:函数返回// 根据多个条件返回不同的内容const renderUserInfo = () => {// 如果用户未登录,显示登录提示if (!isLoggedIn) {return <p>Please log in to see your information</p>;}// 如果用户是管理员,显示管理员信息if (userRole === 'admin') {return <p>Admin user logged in</p>;}// 否则显示普通用户信息return <p>Regular user logged in</p>;};// 返回JSX元素return (<div><h2>Conditional Rendering</h2>{/* 控制按钮 */}<div>{/* 登录/登出按钮 */}<button onClick={() => setIsLoggedIn(!isLoggedIn)}>{isLoggedIn ? 'Logout' : 'Login'}</button>{/* 切换角色按钮 */}<button onClick={() => setUserRole(userRole === 'admin' ? 'user' : 'admin')}>Toggle Role</button>{/* 显示/隐藏详情按钮 */}<button onClick={() => setShowDetails(!showDetails)}>{showDetails ? 'Hide' : 'Show'} Details</button></div>{/* 渲染登录状态 */}{renderLoginStatus()}{/* 渲染管理员面板 */}{renderAdminPanel()}{/* 渲染用户信息 */}{renderUserInfo()}{/* 条件渲染详情 */}{showDetails && (<div><h3>User Details</h3><p>Logged in: {isLoggedIn ? 'Yes' : 'No'}</p><p>Role: {userRole}</p></div>)}</div>);
}// 导出ConditionalRendering组件
export default ConditionalRendering;
3.3 列表渲染(详细注释版)
基本列表渲染(详细注释版)
// src/components/ListRendering.js
// 导入React和useState Hook
import React, { useState } from 'react';// 定义ListRendering组件
function ListRendering() {// 使用useState Hook管理待办事项列表// 初始状态是一个包含几个示例项目的数组const [items, setItems] = useState([{ id: 1, name: 'Apple', price: 1.99, category: 'Fruit' },{ id: 2, name: 'Banana', price: 0.99, category: 'Fruit' },{ id: 3, name: 'Carrot', price: 0.79, category: 'Vegetable' },{ id: 4, name: 'Lettuce', price: 1.49, category: 'Vegetable' }]);// 使用useState Hook管理新项目的表单数据const [newItem, setNewItem] = useState({ name: '', price: '', category: '' });// 添加新项目的函数const addItem = () => {// 检查所有字段是否都已填写if (newItem.name && newItem.price && newItem.category) {// 创建新项目对象const item = {id: Date.now(), // 使用时间戳作为唯一IDname: newItem.name,price: parseFloat(newItem.price), // 将价格转换为数字category: newItem.category};// 使用展开运算符添加新项目到列表setItems([...items, item]);// 清空表单setNewItem({ name: '', price: '', category: '' });}};// 删除项目的函数const removeItem = (id) => {// 使用filter方法过滤掉指定ID的项目setItems(items.filter(item => item.id !== id));};// 计算总价格const totalPrice = items.reduce((sum, item) => sum + item.price, 0);// 返回JSX元素return (<div><h2>Shopping List</h2>{/* 添加新项目的表单 */}<div><inputtype="text"placeholder="Item name"value={newItem.name}onChange={(e) => setNewItem({ ...newItem, name: e.target.value })}/><inputtype="number"placeholder="Price"value={newItem.price}onChange={(e) => setNewItem({ ...newItem, price: e.target.value })}/><selectvalue={newItem.category}onChange={(e) => setNewItem({ ...newItem, category: e.target.value })}><option value="">Select Category</option><option value="Fruit">Fruit</option><option value="Vegetable">Vegetable</option><option value="Dairy">Dairy</option><option value="Meat">Meat</option></select><button onClick={addItem}>Add Item</button></div>{/* 渲染项目列表 */}<ul>{items.map(item => (<li key={item.id}><span>{item.name}</span><span> - ${item.price}</span><span> ({item.category})</span><button onClick={() => removeItem(item.id)}>Remove</button></li>))}</ul>{/* 显示总价格 */}<div><strong>Total: ${totalPrice.toFixed(2)}</strong></div></div>);
}// 导出ListRendering组件
export default ListRendering;
3.4 事件处理(详细注释版)
基本事件处理(详细注释版)
// src/components/EventHandling.js
// 导入React和useState Hook
import React, { useState } from 'react';// 定义EventHandling组件
function EventHandling() {// 使用useState Hook管理状态const [message, setMessage] = useState('');const [count, setCount] = useState(0);const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });// 基本点击事件处理函数const handleClick = () => {setMessage('Button clicked!');};// 带参数的事件处理函数const handleClickWithParam = (param) => {setMessage(`Button clicked with parameter: ${param}`);};// 输入框变化事件处理函数const handleInputChange = (event) => {// event.target.value获取输入框的值setMessage(event.target.value);};// 鼠标移动事件处理函数const handleMouseMove = (event) => {// 获取鼠标位置setMousePosition({x: event.clientX,y: event.clientY});};// 键盘按键事件处理函数const handleKeyPress = (event) => {// 检查是否按下了Enter键if (event.key === 'Enter') {setMessage('Enter key pressed!');}};// 表单提交事件处理函数const handleSubmit = (event) => {// 阻止表单默认提交行为event.preventDefault();setMessage('Form submitted!');};// 返回JSX元素return (<div><h2>Event Handling</h2>{/* 点击事件示例 */}<div><h3>Click Events</h3><button onClick={handleClick}>Click me</button><button onClick={() => handleClickWithParam('Hello')}>Click with param</button><button onClick={(e) => {e.preventDefault();setMessage('Prevented default behavior');}}>Prevent default</button></div>{/* 输入事件示例 */}<div><h3>Input Events</h3><inputtype="text"placeholder="Type something"onChange={handleInputChange}onKeyPress={handleKeyPress}/></div>{/* 表单事件示例 */}<div><h3>Form Events</h3><form onSubmit={handleSubmit}><input type="text" placeholder="Enter text" /><button type="submit">Submit</button></form></div>{/* 鼠标事件示例 */}<divonMouseMove={handleMouseMove}style={{width: '300px',height: '200px',border: '1px solid #ccc',margin: '10px 0',display: 'flex',alignItems: 'center',justifyContent: 'center'}}>Mouse position: {mousePosition.x}, {mousePosition.y}</div>{/* 计数器示例 */}<div><h3>Counter</h3><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>+</button><button onClick={() => setCount(count - 1)}>-</button><button onClick={() => setCount(0)}>Reset</button></div>{/* 显示消息 */}<div><p>Message: {message}</p></div></div>);
}// 导出EventHandling组件
export default EventHandling;
第四部分:实践项目(详细注释版)
项目:简单的待办事项应用
主应用组件(详细注释版)
// src/App.js
// 导入React和useState Hook
import React, { useState } from 'react';
// 导入样式文件
import './App.css';// 定义主应用组件
function App() {// 使用useState Hook管理待办事项列表// 初始状态是一个空数组const [todos, setTodos] = useState([]);// 使用useState Hook管理新待办事项的输入const [newTodo, setNewTodo] = useState('');// 添加新待办事项的函数const addTodo = () => {// 检查输入是否为空if (newTodo.trim()) {// 创建新的待办事项对象const todo = {id: Date.now(), // 使用时间戳作为唯一IDtext: newTodo.trim(), // 去除首尾空格completed: false // 初始状态为未完成};// 使用展开运算符添加新待办事项setTodos([...todos, todo]);// 清空输入框setNewTodo('');}};// 切换待办事项完成状态的函数const toggleTodo = (id) => {// 使用map方法更新指定ID的待办事项setTodos(todos.map(todo =>todo.id === id ? { ...todo, completed: !todo.completed } : todo));};// 删除待办事项的函数const deleteTodo = (id) => {// 使用filter方法过滤掉指定ID的待办事项setTodos(todos.filter(todo => todo.id !== id));};// 计算已完成和未完成的待办事项数量const completedCount = todos.filter(todo => todo.completed).length;const totalCount = todos.length;// 返回应用JSXreturn (<div className="App"><header className="App-header"><h1>Simple Todo App</h1></header><main>{/* 添加新待办事项的表单 */}<div className="todo-form"><inputtype="text"placeholder="Add a new todo"value={newTodo}onChange={(e) => setNewTodo(e.target.value)}onKeyPress={(e) => e.key === 'Enter' && addTodo()}/><button onClick={addTodo}>Add Todo</button></div>{/* 待办事项统计 */}<div className="todo-stats"><p>Total: {totalCount}</p><p>Completed: {completedCount}</p><p>Remaining: {totalCount - completedCount}</p></div>{/* 待办事项列表 */}<div className="todo-list">{todos.length === 0 ? (<p>No todos yet. Add one above!</p>) : (todos.map(todo => (<div key={todo.id} className={`todo-item ${todo.completed ? 'completed' : ''}`}><inputtype="checkbox"checked={todo.completed}onChange={() => toggleTodo(todo.id)}/><span className="todo-text">{todo.text}</span><button onClick={() => deleteTodo(todo.id)}>Delete</button></div>)))}</div></main></div>);
}// 导出App组件
export default App;
样式文件(详细注释版)
/* src/App.css */
/* 应用主容器样式 */
.App {text-align: center;max-width: 600px;margin: 0 auto;padding: 20px;font-family: Arial, sans-serif;
}/* 应用头部样式 */
.App-header {margin-bottom: 30px;
}.App-header h1 {color: #333;margin-bottom: 10px;
}/* 待办事项表单样式 */
.todo-form {display: flex;gap: 10px;margin-bottom: 20px;
}.todo-form input {flex: 1;padding: 10px;border: 1px solid #ddd;border-radius: 4px;font-size: 16px;
}.todo-form button {padding: 10px 20px;background-color: #007bff;color: white;border: none;border-radius: 4px;cursor: pointer;font-size: 16px;
}.todo-form button:hover {background-color: #0056b3;
}/* 待办事项统计样式 */
.todo-stats {display: flex;justify-content: space-around;margin-bottom: 20px;padding: 15px;background-color: #f8f9fa;border-radius: 8px;
}.todo-stats p {margin: 0;font-weight: bold;color: #495057;
}/* 待办事项列表样式 */
.todo-list {text-align: left;
}.todo-item {display: flex;align-items: center;gap: 10px;padding: 15px;margin-bottom: 10px;background-color: white;border: 1px solid #ddd;border-radius: 8px;box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}.todo-item.completed {background-color: #f8f9fa;opacity: 0.7;
}.todo-item.completed .todo-text {text-decoration: line-through;color: #6c757d;
}.todo-item input[type="checkbox"] {width: 18px;height: 18px;cursor: pointer;
}.todo-text {flex: 1;font-size: 16px;
}.todo-item button {padding: 5px 10px;background-color: #dc3545;color: white;border: none;border-radius: 4px;cursor: pointer;font-size: 14px;
}.todo-item button:hover {background-color: #c82333;
}
练习题目
基础练习
- 组件创建练习
// 练习1:创建一个Welcome组件,接收name属性
function Welcome({ name }) {// 实现组件,显示欢迎信息return (<div><h1>Welcome, {name}!</h1><p>Nice to meet you!</p></div>);
}// 练习2:创建一个Button组件,支持不同样式
function Button({ children, variant = 'primary', onClick }) {// 实现组件,支持primary、secondary、danger等样式const getClassName = () => {switch (variant) {case 'primary':return 'btn-primary';case 'secondary':return 'btn-secondary';case 'danger':return 'btn-danger';default:return 'btn-primary';}};return (<button className={getClassName()} onClick={onClick}>{children}</button>);
}
- JSX练习
// 练习3:创建一个TodoItem组件
function TodoItem({ todo, onToggle, onDelete }) {// 实现待办事项显示和操作return (<div className={`todo-item ${todo.completed ? 'completed' : ''}`}><inputtype="checkbox"checked={todo.completed}onChange={() => onToggle(todo.id)}/><span className="todo-text">{todo.text}</span><button onClick={() => onDelete(todo.id)}>Delete</button></div>);
}// 练习4:创建一个UserList组件
function UserList({ users }) {// 实现用户列表显示return (<div className="user-list">{users.map(user => (<div key={user.id} className="user-item"><h3>{user.name}</h3><p>Email: {user.email}</p><p>Age: {user.age}</p></div>))}</div>);
}
进阶练习
- 综合应用
// 练习5:创建一个完整的待办事项应用
// 包含:添加、删除、完成、编辑功能
// 支持:过滤、排序、搜索
function TodoApp() {const [todos, setTodos] = useState([]);const [filter, setFilter] = useState('all');const [searchTerm, setSearchTerm] = useState('');// 实现所有功能// ...
}
学习检查点
完成标准
- 能够创建React项目
- 理解组件和Props的概念
- 掌握JSX语法
- 能够处理事件
- 完成所有练习题目
自我测试
- React组件有哪两种写法?
- Props和State的区别是什么?
- JSX中如何写注释?
- 事件处理函数中如何阻止默认行为?
扩展阅读
推荐资源
- React官方文档
- React学习路径
- JSX语法指南
明日预告
明天我们将学习:
- useState Hook深入
- useEffect Hook
- 组件生命周期
- 条件渲染和列表渲染
- 为Hooks学习做准备