跳到主要内容

阶段4:状态State

🎯 学习目标

理解State(状态)的概念,学会使用useState钩子,让组件具备"记忆"和"变化"的能力。


一、什么是State?

Props vs State:有什么区别?

对比项PropsState
比喻父母给的零花钱(被动接收)自己的存钱罐(主动管理)
来源从父组件传入组件自己内部定义
能否修改❌ 只读,不能改✅ 可以用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;

运行结果:

  1. 初始显示"张三"和简介,有"编辑"按钮
  2. 点"编辑" → 出现输入框,可修改
  3. 改完点"保存" → 显示新内容

涉及知识点:

  • 3个State变量
  • 条件渲染(根据isEditing显示不同UI)
  • 表单受控组件

✅ 阶段4总结

你已经学会:

  1. ✓ State的概念(组件的"记忆")
  2. ✓ useState的语法:const [value, setValue] = useState(初始值)
  3. ✓ 修改State会触发页面重新渲染
  4. ✓ 受控组件(输入框的值由State控制)
  5. ✓ 多个State的管理

核心公式:

State变化 → React重新渲染组件 → 页面自动更新

验收标准:

  • 能用useState创建State变量
  • 能通过setState修改State
  • 理解为什么改State要用setState,不能直接赋值

Props vs State 对比表:

PropsState
能否修改❌ 只读✅ 可改
谁拥有父组件当前组件
改变时父组件传新值调用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>
);
}