3.2.2 积木的状态谁来管——State 管理
一句话破题
State 是组件的"记忆",让 UI 能够响应用户交互和数据变化而更新。
核心价值
Props 是外部传入的只读数据,而 State 是组件内部可以自主管理和修改的数据。当 State 变化时,React 会自动重新渲染组件。
useState 基础
tsx
'use client' // 使用 Hooks 必须是 Client Component
import { useState } from 'react'
function Counter() {
// 声明状态:[当前值, 更新函数] = useState(初始值)
const [count, setCount] = useState(0)
return (
<div>
<p>当前计数:{count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(prev => prev - 1)}>-1</button>
</div>
)
}更新状态的两种方式
tsx
// 方式一:直接设置新值
setCount(5)
// 方式二:基于前一个状态更新(推荐,避免闭包陷阱)
setCount(prev => prev + 1)什么时候用函数式更新?
当新状态依赖于旧状态时,务必使用函数式更新:
tsx
// 错误:可能因闭包导致意外行为
const handleClick = () => {
setCount(count + 1)
setCount(count + 1) // 可能不会 +2
}
// 正确:保证基于最新状态
const handleClick = () => {
setCount(prev => prev + 1)
setCount(prev => prev + 1) // 一定会 +2
}对象和数组状态
对象状态:
tsx
const [user, setUser] = useState({ name: '', age: 0 })
// 更新对象的某个字段(必须创建新对象)
setUser(prev => ({ ...prev, name: '张三' }))数组状态:
tsx
const [items, setItems] = useState<string[]>([])
// 添加
setItems(prev => [...prev, 'new item'])
// 删除
setItems(prev => prev.filter((_, i) => i !== index))
// 更新
setItems(prev => prev.map((item, i) => i === index ? 'updated' : item))useReducer:复杂状态的救星
当状态逻辑变得复杂时,useReducer 比多个 useState 更清晰:
tsx
'use client'
import { useReducer } from 'react'
// 定义状态类型
interface State {
count: number
loading: boolean
error: string | null
}
// 定义 Action 类型
type Action =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'setLoading'; payload: boolean }
| { type: 'setError'; payload: string }
// Reducer 函数
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 }
case 'decrement':
return { ...state, count: state.count - 1 }
case 'setLoading':
return { ...state, loading: action.payload }
case 'setError':
return { ...state, error: action.payload }
default:
return state
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, {
count: 0,
loading: false,
error: null
})
return (
<div>
<p>{state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+1</button>
</div>
)
}useState vs useReducer
| 场景 | 推荐 |
|---|---|
| 单一值(数字、字符串、布尔) | useState |
| 简单对象(2-3 个字段) | useState |
| 复杂对象(多字段、多种更新方式) | useReducer |
| 状态更新有明确的"动作"语义 | useReducer |
| 需要集中管理状态逻辑 | useReducer |
状态提升
当多个组件需要共享状态时,将状态提升到它们的共同父组件:
tsx
// 父组件持有状态
function Parent() {
const [value, setValue] = useState('')
return (
<div>
<Input value={value} onChange={setValue} />
<Display value={value} />
</div>
)
}
// 子组件通过 Props 接收状态和更新函数
function Input({ value, onChange }: { value: string; onChange: (v: string) => void }) {
return <input value={value} onChange={e => onChange(e.target.value)} />
}
function Display({ value }: { value: string }) {
return <p>当前输入:{value}</p>
}AI 协作指南
核心意图:让 AI 帮你设计组件的状态结构。
需求定义公式:
- 功能描述:组件需要记住 [哪些数据]
- 交互方式:用户可以通过 [操作] 改变状态
- 预期效果:状态改变时 UI 如何响应
关键术语:useState、useReducer、dispatch、状态提升、函数式更新
交互策略:
- 先让 AI 分析需要哪些状态
- 判断用 useState 还是 useReducer
- 实现状态更新逻辑
- 处理状态提升(如需要)
避坑指南
- State 更新是异步的:设置后不能立即读取新值
- 不要直接修改状态:
state.count++是错的,必须用 setter - 对象/数组必须创建新引用:
{...obj}或[...arr] - 避免过多 useState:超过 3-4 个考虑用 useReducer 或自定义 Hook
验收清单
- [ ] 正确使用
'use client'声明 - [ ] 对象/数组状态更新时创建新引用
- [ ] 依赖前值的更新使用函数式写法
- [ ] 复杂状态逻辑使用 useReducer
- [ ] 共享状态正确提升到公共父组件
