第3课:状态管理与事件处理
第3课:状态管理与事件处理
学习目标
- 掌握
useState
Hook的使用 - 理解组件事件处理机制
- 实现表单输入与状态绑定
- 完成任务添加功能原型
一、useState基础
1. 创建第一个状态
新建src/Counter.js
:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>当前计数:{count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
<button onClick={() => setCount(count - 1)}>减少</button>
</div>
);
}
export default Counter;
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me+1</button>
<button onClick={() => setCount(count - 1)}>Click me-1</button>
</div>
);
}
export default Counter;
// // import React from 'react'
关键点解析:
useState
返回当前状态和更新函数- 状态更新会触发组件重新渲染
- 永远不要直接修改状态,必须使用状态更新函数
2. 在App组件中使用
import Counter from './Counter';
function App() {
return (
<div className="App">
<Counter />
</div>
);
}
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import HelloWorld from './HelloWorld.jsx'
import Introduction from './Introduction.jsx'
import JsxDemo from './JsxDemo.jsx'
import Greeting from './Greeting.jsx'
import InlineStyleDemo from './InlineStyleDemo.jsx'
import Card from './Card.jsx';
import TaskCard from './TaskCard.jsx'
import Counter from './Counter.jsx'
function App() {
return (
<>
<div className='App'>
<Counter />
</div>
</>
)
}
export default App
二、表单处理基础
1. 受控组件实现
新建src/InputDemo.js
:
import { useState } from 'react';
function InputDemo() {
const [inputValue, setInputValue] = useState('');
const [displayText, setDisplayText] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
setDisplayText(inputValue);
setInputValue('');
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="输入内容..."
/>
<button type="submit">提交</button>
</form>
{displayText && <p>你输入的内容是:{displayText}</p>}
</div>
);
}
export default InputDemo;
import { useState } from "react";
function InputDemo() {
const [inputValue, setInputValue] = useState("");
const [displayValue, setDisplayValue] = useState("");
const handleChange = (event) => {
event.preventDefault();
setDisplayValue(inputValue);
setInputValue('');
};
return (
<div>
<h1>Input Demo</h1>
<form onSubmit={handleChange}>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="Type something..."
/>
<button type="submit">Submit</button>
</form>
<p>You typed: {displayValue}</p>
</div>
);
}
export default InputDemo;
// // // import React from 'react'
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import HelloWorld from './HelloWorld.jsx'
import Introduction from './Introduction.jsx'
import JsxDemo from './JsxDemo.jsx'
import Greeting from './Greeting.jsx'
import InlineStyleDemo from './InlineStyleDemo.jsx'
import Card from './Card.jsx';
import TaskCard from './TaskCard.jsx'
import Counter from './Counter.jsx'
import InputDemo from './InputDemo.jsx'
function App() {
return (
<>
<div className='App'>
<InputDemo />
</div>
</>
)
}
export default App
2. 多字段表单处理
function UserForm() {
const [formData, setFormData] = useState({
username: '',
email: '',
password: ''
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('表单数据:', formData);
};
return (
<form onSubmit={handleSubmit}>
<input
name="username"
value={formData.username}
onChange={handleChange}
placeholder="用户名"
/>
<input
name="email"
type="email"
value={formData.email}
onChange={handleChange}
placeholder="邮箱"
/>
<input
name="password"
type="password"
value={formData.password}
onChange={handleChange}
placeholder="密码"
/>
<button type="submit">注册</button>
</form>
);
}
import { useState } from "react";
function UserForm() {
const [formData, setFormData] = useState({
name: "",
email: "",
password: "",
confirmPassword: "",
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prevData) => ({
...prevData,
[name]: value,
}));
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('The form data:',formData);
// Perform form validation here
// If validation passes, submit the form data
// If validation fails, display error messages
};
return (
<>
<form onSubmit={handleSubmit}>
<input name="username"
value={formData.username}
onChange={handleChange}
placeholder="Username"/>
<input name="email"
type="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"/>
<input name="password"
type="password"
value={formData.password}
onChange={handleChange}
placeholder="Password"/>
<input name="confirmPassword"
type="password"
value={formData.confirmPassword}
onChange={handleChange}
placeholder="Confirm Password"/>
<button type="submit">Submit</button>
</form>
<p>Username: {formData.username}</p>
</>
);
}
export default UserForm;
// // // import React from 'react'
```bash
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import HelloWorld from './HelloWorld.jsx'
import Introduction from './Introduction.jsx'
import JsxDemo from './JsxDemo.jsx'
import Greeting from './Greeting.jsx'
import InlineStyleDemo from './InlineStyleDemo.jsx'
import Card from './Card.jsx';
import TaskCard from './TaskCard.jsx'
import Counter from './Counter.jsx'
import InputDemo from './InputDemo.jsx'
import UserForm from './UserForm.jsx'
function App() {
return (
<>
<div className='App'>
<UserForm />
</div>
</>
)
}
export default App
- 以下未验证,明天验证:
三、任务管理系统集成
1. 状态提升到App组件
修改App.js
:
import { useState } from 'react';
import TaskCard from './TaskCard';
function App() {
const [tasks, setTasks] = useState([
{ id: 1, text: '学习React基础', completed: false },
{ id: 2, text: '完成课后练习', completed: true }
]);
return (
<div className="App">
<h1>任务管理系统</h1>
<div className="task-list">
{tasks.map(task => (
<TaskCard
key={task.id}
title={task.text}
completed={task.completed}
/>
))}
</div>
</div>
);
}
2. 添加任务功能组件
新建src/AddTask.js
:
import { useState } from 'react';
function AddTask({ onAddTask }) {
const [newTask, setNewTask] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (!newTask.trim()) return;
onAddTask({
id: Date.now(),
text: newTask,
completed: false
});
setNewTask('');
};
return (
<form onSubmit={handleSubmit} className="add-task-form">
<input
type="text"
value={newTask}
onChange={(e) => setNewTask(e.target.value)}
placeholder="添加新任务..."
/>
<button type="submit">添加</button>
</form>
);
}
export default AddTask;
3. 完整功能集成
更新App.js
:
function App() {
const [tasks, setTasks] = useState([
{ id: 1, text: '学习React基础', completed: false },
{ id: 2, text: '完成课后练习', completed: true }
]);
const addTask = (newTask) => {
setTasks([...tasks, newTask]);
};
const toggleTask = (taskId) => {
setTasks(tasks.map(task =>
task.id === taskId
? { ...task, completed: !task.completed }
: task
));
};
return (
<div className="App">
<h1>任务管理系统</h1>
<AddTask onAddTask={addTask} />
<div className="task-list">
{tasks.map(task => (
<TaskCard
key={task.id}
title={task.text}
completed={task.completed}
onToggle={() => toggleTask(task.id)}
/>
))}
</div>
</div>
);
}
更新TaskCard.js
:
function TaskCard({ title, completed, onToggle }) {
return (
<div className={`task-card ${completed ? 'completed' : ''}`}>
<h3>{title}</h3>
<div className="task-actions">
<button onClick={onToggle}>
{completed ? '标记未完成' : '标记完成'}
</button>
</div>
</div>
);
}
四、样式优化
添加src/App.css
:
.App {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.add-task-form {
margin-bottom: 20px;
display: flex;
gap: 10px;
}
.add-task-form input {
flex: 1;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.add-task-form button {
padding: 8px 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.task-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.task-card {
/* 原有样式基础上添加 */
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
}
.task-actions button {
padding: 6px 12px;
background-color: #2196F3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.task-card.completed {
opacity: 0.7;
}
五、练习任务
-
实现删除任务功能:
- 为每个任务卡片添加删除按钮
- 创建
deleteTask
方法 - 使用filter方法更新状态
-
为添加任务表单添加验证:
- 输入不能为空
- 输入长度不能超过50字符
- 显示实时字符计数
-
(挑战)添加任务分类功能:
- 添加选择器选择任务类型(工作/学习/生活)
- 在任务卡片显示对应类型图标
六、常见问题解答
Q: 为什么状态更新后界面没有变化?
A: 检查是否:
- 正确使用了状态更新函数
- 状态值确实是新的(对于对象/数组需要创建新引用)
- 组件是否正确接收了props
Q: 如何处理多个状态?
A: 可以:
- 使用多个
useState
- 合并相关状态到一个对象
- 当状态相互关联时,优先考虑合并
Q: 为什么表单提交后页面刷新?
A: 确保:
- 表单提交处理函数中有
e.preventDefault()
- 按钮类型不是
submit
(如果不需要表单提交)
七、课程总结
今天我们掌握了:
useState
Hook的基本用法- 表单事件处理流程
- 组件间的状态传递
- 实现了一个交互式的任务管理系统原型
课后作业
- 完成删除功能实现
- 为任务添加优先级属性(高/中/低)
- 尝试使用本地存储保存任务数据(提前预习下节课内容)
下一节课我们将学习列表渲染与条件渲染的进阶技巧!