React
React 状态提升
状态提升是 React 的一种重要设计模式,指将多个组件需要共享的状态提升到最近的公共父组件中管理,通过 props 将状态和更新函数传递给子组件,确保数据的一致性。
一、核心概念
问题:多个子组件各自维护相同的状态,容易导致数据不同步。
解决方案:将共享状态集中到公共父组件,由父组件统一管理,通过 props 向下传递。
优势:
- 数据唯一真实来源(Single Source of Truth)
- 避免状态不一致
- 组件间通信更清晰
二、基本实现
温度转换示例
interface InputProps {
scale: 'c' | 'f'
value: string
onChange: (value: string) => void
}
function TemperatureInput({ scale, value, onChange }: InputProps) {
const label = scale === 'c' ? '摄氏度' : '华氏度'
return (
<div>
<label>
输入 {label}:
<input
type="number"
value={value}
onChange={(e) => onChange(e.target.value)}
/>
</label>
</div>
)
}
function TemperatureCalculator() {
const [celsius, setCelsius] = React.useState('')
const [fahrenheit, setFahrenheit] = React.useState('')
const handleCelsiusChange = (temp: string) => {
setCelsius(temp)
// 计算华氏度
if (temp) {
const f = (parseFloat(temp) * 9) / 5 + 32
setFahrenheit(f.toFixed(2))
} else {
setFahrenheit('')
}
}
const handleFahrenheitChange = (temp: string) => {
setFahrenheit(temp)
// 计算摄氏度
if (temp) {
const c = ((parseFloat(temp) - 32) * 5) / 9
setCelsius(c.toFixed(2))
} else {
setCelsius('')
}
}
return (
<div>
<TemperatureInput
scale="c"
value={celsius}
onChange={handleCelsiusChange}
/>
<TemperatureInput
scale="f"
value={fahrenheit}
onChange={handleFahrenheitChange}
/>
<p>当前温度:{celsius}°C = {fahrenheit}°F</p>
</div>
)
}三、常见使用场景
表单数据共享
多个表单字段需要共享验证逻辑或相互影响时:
interface FormData {
username: string
email: string
password: string
}
function LoginForm() {
const [form, setForm] = React.useState<FormData>({
username: '',
email: '',
password: '',
})
const handleChange = (field: keyof FormData, value: string) => {
setForm({ ...form, [field]: value })
}
return (
<div>
<input
value={form.username}
onChange={(e) => handleChange('username', e.target.value)}
placeholder="用户名"
/>
<input
type="email"
value={form.email}
onChange={(e) => handleChange('email', e.target.value)}
placeholder="邮箱"
/>
<input
type="password"
value={form.password}
onChange={(e) => handleChange('password', e.target.value)}
placeholder="密码"
/>
</div>
)
}组件联动
一个组件的状态变化影响其他组件时:
interface Product {
id: number
name: string
price: number
selected: boolean
}
function ProductList({ products, onSelect }: any) {
return (
<ul>
{products.map((p: Product) => (
<li key={p.id}>
<button onClick={() => onSelect(p.id)}>{p.name}</button>
</li>
))}
</ul>
)
}
function ProductDetail({ selected }: any) {
return <div>{selected ? `已选择: ${selected.name}` : '请选择商品'}</div>
}
function ProductPage() {
const [products] = React.useState<Product[]>([
{ id: 1, name: '商品A', price: 99, selected: false },
{ id: 2, name: '商品B', price: 199, selected: false },
])
const [selectedId, setSelectedId] = React.useState<number | null>(null)
const selected = products.find((p) => p.id === selectedId)
return (
<div>
<ProductList products={products} onSelect={setSelectedId} />
<ProductDetail selected={selected} />
</div>
)
}四、何时使用状态提升
应该提升:
- 两个或以上的子组件需要访问相同状态
- 多个子组件依赖同一份数据
- 组件层级不太深(1-2 层)
不应该提升:
- 只有一个组件使用某个状态
- 需要跨很多层级传递(导致 props drilling)
- 全局共享的状态(考虑 Context 或 Redux)
五、性能优化
使用 React.memo 避免不必要的重渲染
const TemperatureInput = React.memo(
({ scale, value, onChange }: InputProps) => {
return (
<input
type="number"
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={scale === 'c' ? '摄氏度' : '华氏度'}
/>
)
}
)提取回调函数,避免每次创建新函数
function Parent() {
const [value, setValue] = React.useState('')
// ✗ 错误:每次渲染都创建新函数
// const handleChange = (val: string) => setValue(val)
// ✓ 正确:使用 useCallback 缓存函数
const handleChange = React.useCallback((val: string) => {
setValue(val)
}, [])
return <Child value={value} onChange={handleChange} />
}六、vs 其他方案
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 状态提升 | 简单、浅层组件通信 | 直观易懂,无额外依赖 | 多层传递会导致 props drilling |
| Context API | 深层组件树、全局主题 | 避免 props drilling,原生支持 | 会导致不必要的重渲染 |
| Redux | 复杂全局状态 | 强大的中间件系统,时间旅行调试 | 学习成本高,代码冗长 |
| Zustand | 轻量级全局状态 | 简洁易用,体积小 | 社区规模小于 Redux |
选择建议:
- 父子或兄弟组件通信 → 状态提升
- 深层组件树、全局主题 → Context API
- 复杂状态、中间件需求 → Redux
- 轻量级全局状态 → Zustand
一句话记住:状态提升是将组件间的共享状态上移到公共父组件管理,保证数据唯一真实来源。当层级变深或状态复杂时,考虑迁移到 Context 或专业状态管理库。