阶段4:状态State
🎯 学习目标
理解State(状态)的概念,学会使用useState钩子,让组件具备"记忆"和"变化"的能力。
一、什么是State?
Props vs State:有什么区别?
| 对比项 | Props | State |
|---|---|---|
| 比喻 | 父母给的零花钱(被动接收) | 自己的存钱罐(主动管理) |
| 来源 | 从父组件传入 | 组件自己内部定义 |
| 能否修改 | ❌ 只读,不能改 | ✅ 可以用setState改 |
| 用途 | 接收外部数据 | 管理组件内部数据 |
通俗解释:
- Props: 别人告诉你的信息(你家地址、你的名字)
- State: 你自己的状态(你饿不饿、你累不累)
// Props:从外部接收,不能改
<Counter start={0} /> // start是Props,Counter组件不能改它
// State:组件自己的数据,可以改
function Counter() {
const [count, setCount] = useState(0); // count是State,可以用setCount修改
}
二、useState:给组件添加"记忆"
基本语法
import { useState } from 'react'; // 先导入
function MyComponent() {
// 语法:const [状态变量, 修改函数] = useState(初始值);
const [count, setCount] = useState(0);
// count:当前值
// setCount:修改count的函数
// 0:初始值
}
类比: 就像定义一个"有记忆"的变量
// 普通变量:组件重新渲染后值会丢失
let count = 0;
// State变量:React会帮你记住,重新渲染后值还在
const [count, setCount] = useState(0);
三、实战案例1:计数器
目标:点击按钮,数字+1
创建 src/Counter.js:
import { useState } from 'react'; // 第1步:导入useState
function Counter() {
// 第2步:定义State
const [count, setCount] = useState(0); // 初始值是0
// 第3步:定义修改State的函数
const handleAdd = () => {
setCount(count + 1); // 调用setCount修改count
// ⚠️ 千万不要写 count = count + 1,这样不会触发页面更新!
};
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
{/* 显示当前count */}
<h1 style={{ fontSize: '48px', color: '#333' }}>
{count}
</h1>
{/* 点击按钮调用handleAdd */}
<button
onClick={handleAdd}
style={{
padding: '10px 20px',
fontSize: '18px',
backgroundColor: '#4CAF50',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: 'pointer'
}}
>
+1
</button>
</div>
);
}
export default Counter;
使用 - 在 App.js:
import Counter from './Counter';
function App() {
return <Counter />;
}
export default App;
运行结果:
- 初始显示数字 0
- 每次点击按钮,数字加1
- 页面自动更新(这就是State的魔法!)
为什么用State,不用普通变量?
// ❌ 错误示范:用普通变量
function BrokenCounter() {
let count = 0; // 普通变量
const handleAdd = () => {
count = count + 1;
console.log(count); // 控制台能看到count变了
};
return <h1>{count}</h1>; // 但页面不会更新!
}
// ✅ 正确:用State
function Counter() {
const [count, setCount] = useState(0); // State
const handleAdd = () => {
setCount(count + 1); // 调用setCount会触发页面重新渲染
};
return <h1>{count}</h1>; // 页面会更新!
}
关键: 只有用 setState 修改State,React才知道"哦,数据变了,我要更新页面"。
四、实战案例2:开关按钮
目标:点击按钮切换开/关状态
创建 src/ToggleButton.js:
import { useState } from 'react';
function ToggleButton() {
// State可以是任何类型:数字、字符串、布尔值、对象、数组...
const [isOn, setIsOn] = useState(false); // 初始是false(关)
const handleToggle = () => {
setIsOn(!isOn); // 取反:true变false,false变true
};
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
{/* 根据isOn显示不同内容 */}
<h2>灯光状态: {isOn ? '💡 开启' : '🌙 关闭'}</h2>
<button
onClick={handleToggle}
style={{
padding: '15px 30px',
fontSize: '18px',
backgroundColor: isOn ? '#f44336' : '#4CAF50', // 开启时红色,关闭时绿色
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: 'pointer'
}}
>
{isOn ? '关闭' : '开启'}
</button>
</div>
);
}
export default ToggleButton;
运行结果:
- 初始显示"灯光状态: 🌙 关闭",按钮是绿色
- 点击按钮后变成"灯光状态: 💡 开启",按钮变红色
- 再点击又切换回去
五、实战案例3:输入框实时显示
目标:输入框输入什么,下面就显示什么
创建 src/InputDemo.js:
import { useState } from 'react';
function InputDemo() {
const [text, setText] = useState(''); // 初始是空字符串
// 当输入框内容变化时触发
const handleChange = (event) => {
const newText = event.target.value; // 获取输入框的值
setText(newText); // 更新State
};
return (
<div style={{ padding: '20px' }}>
<h3>实时显示输入内容:</h3>
{/* 输入框 */}
<input
type="text"
value={text} // ⚠️ 重要:把State绑定到value
onChange={handleChange} // 监听变化
placeholder="请输入内容..."
style={{
padding: '10px',
fontSize: '16px',
width: '300px',
border: '2px solid #ddd',
borderRadius: '5px'
}}
/>
{/* 实时显示 */}
<p style={{ marginTop: '20px', fontSize: '18px', color: '#333' }}>
你输入的是: <strong>{text}</strong>
</p>
{/* 显示字数 */}
<p style={{ color: '#666' }}>
字数: {text.length}
</p>
</div>
);
}
export default InputDemo;
运行结果:
- 在输入框输入"Hello",下面立即显示"你输入的是: Hello"
- 字数也实时更新
关键点:
value={text}: 受控组件,输入框的值由State控制onChange={handleChange}: 每次输入都触发,更新State- State变化 → 页面自动更新
六、State更新的进阶技巧
技巧1:基于旧值更新State
function Counter() {
const [count, setCount] = useState(0);
// ❌ 方式1:直接用count(可能有问题)
const add1 = () => {
setCount(count + 1);
};
// ✅ 方式2:用函数(推荐,更安全)
const add2 = () => {
setCount(prevCount => prevCount + 1);
// prevCount是更新前的值,React保证它是最新的
};
// 如果连续调用,方式2更准确
const add3Times = () => {
setCount(prev => prev + 1); // +1
setCount(prev => prev + 1); // 再+1
setCount(prev => prev + 1); // 再+1
// 最终+3
};
}
技巧2:多个State变量
function UserForm() {
// 可以定义多个State
const [name, setName] = useState('');
const [age, setAge] = useState(18);
const [email, setEmail] = useState('');
return (
<div>
<input value={name} onChange={e => setName(e.target.value)} />
<input value={age} onChange={e => setAge(Number(e.target.value))} />
<input value={email} onChange={e => setEmail(e.target.value)} />
</div>
);
}
技巧3:State是对象时
function Profile() {
const [user, setUser] = useState({
name: '小明',
age: 18,
city: '北京'
});
// ❌ 错误:直接改对象(不会触发更新)
const wrongUpdate = () => {
user.age = 19; // 不要这样!
};
// ✅ 正确:创建新对象
const correctUpdate = () => {
setUser({
...user, // 复制旧值
age: 19 // 只改age
});
};
}
七、综合案例:交互式个人资料卡
目标:可以编辑姓名和简介的卡片
创建 src/EditableCard.js:
import { useState } from 'react';
function EditableCard() {
// 定义两个State
const [name, setName] = useState('张三');
const [bio, setBio] = useState('这是一段简介');
const [isEditing, setIsEditing] = useState(false); // 是否在编辑模式
return (
<div style={{
border: '2px solid #4CAF50',
borderRadius: '10px',
padding: '20px',
maxWidth: '400px',
margin: '20px auto'
}}>
<h2>个人资料</h2>
{isEditing ? (
// 编辑模式:显示输入框
<div>
<div style={{ marginBottom: '10px' }}>
<label>姓名:</label>
<input
value={name}
onChange={e => setName(e.target.value)}
style={{ marginLeft: '10px', padding: '5px' }}
/>
</div>
<div style={{ marginBottom: '10px' }}>
<label>简介:</label>
<textarea
value={bio}
onChange={e => setBio(e.target.value)}
style={{
marginLeft: '10px',
padding: '5px',
width: '250px',
height: '60px'
}}
/>
</div>
<button
onClick={() => setIsEditing(false)}
style={{
padding: '8px 16px',
backgroundColor: '#4CAF50',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: 'pointer'
}}
>
保存
</button>
</div>
) : (
// 查看模式:显示内容
<div>
<h3>{name}</h3>
<p style={{ color: '#666' }}>{bio}</p>
<button
onClick={() => setIsEditing(true)}
style={{
padding: '8px 16px',
backgroundColor: '#2196F3',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: 'pointer'
}}
>
编辑
</button>
</div>
)}
</div>
);
}
export default EditableCard;
运行结果:
- 初始显示"张三"和简介,有"编辑"按钮
- 点"编辑" → 出现输入框,可修改
- 改完点"保存" → 显示新内容
涉及知识点:
- 3个State变量
- 条件渲染(根据isEditing显示不同UI)
- 表单受控组件
✅ 阶段4总结
你已经学会:
- ✓ State的概念(组件的"记忆")
- ✓ useState的语法:
const [value, setValue] = useState(初始值) - ✓ 修改State会触发页面重新渲染
- ✓ 受控组件(输入框的值由State控制)
- ✓ 多个State的管理
核心公式:
State变化 → React重新渲染组件 → 页面自动更新
验收标准:
- 能用useState创建State变量
- 能通过setState修改State
- 理解为什么改State要用setState,不能直接赋值
Props vs State 对比表:
| Props | State | |
|---|---|---|
| 能否修改 | ❌ 只读 | ✅ 可改 |
| 谁拥有 | 父组件 | 当前组件 |
| 改变时 | 父组件传新值 | 调用setState |
| 用途 | 接收外部数据 | 管理内部状态 |
下一步: 现在组件能"记住"数据了,但还不能响应复杂的用户操作。下一阶段我们将深入学习事件处理,包括点击、输入、提交等常见交互!
💡 练习题
做一个 ColorPicker 组件:
- 有3个按钮:红、绿、蓝
- 点击按钮,下方的方块变成对应颜色
- 提示:用State存储当前颜色
// 参考结构
function ColorPicker() {
const [color, setColor] = useState('gray');
return (
<div>
<button onClick={() => setColor('red')}>红色</button>
{/* 其他按钮... */}
<div style={{
width: '100px',
height: '100px',
backgroundColor: color
}}></div>
</div>
);
}