FumadocsZDecode
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 或专业状态管理库。

On this page