⚠️ Alpha内测版本警告:此为早期内部构建版本,尚不完整且可能存在错误,欢迎大家提Issue反馈问题或建议
Skip to content

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 如何响应

关键术语useStateuseReducerdispatch、状态提升、函数式更新

交互策略

  1. 先让 AI 分析需要哪些状态
  2. 判断用 useState 还是 useReducer
  3. 实现状态更新逻辑
  4. 处理状态提升(如需要)

避坑指南

  1. State 更新是异步的:设置后不能立即读取新值
  2. 不要直接修改状态state.count++ 是错的,必须用 setter
  3. 对象/数组必须创建新引用{...obj}[...arr]
  4. 避免过多 useState:超过 3-4 个考虑用 useReducer 或自定义 Hook

验收清单

  • [ ] 正确使用 'use client' 声明
  • [ ] 对象/数组状态更新时创建新引用
  • [ ] 依赖前值的更新使用函数式写法
  • [ ] 复杂状态逻辑使用 useReducer
  • [ ] 共享状态正确提升到公共父组件