首页 / React 19 入门教程 / 状态结构选择

React 19 入门教程

状态结构选择

本教程共 50 篇 · 第 20 篇 · 更新于 2026-07-29 · 约 7 分钟阅读

React状态结构状态设计冗余状态状态组织

20. 状态结构选择

本节目标:学会合理组织 state 结构,避免冗余和矛盾,让状态既简洁又不容易出错。

组织 state 有三个原则,记住就行:

合并相关状态

两个 state 总是一起变化,合并成一个:

// 不好:两个独立 state
const [x, setX] = useState(0);
const [y, setY] = useState(0);

// 好:合并成一个
const [position, setPosition] = useState({ x: 0, y: 0 });

// 更新时
setPosition({ x: 100, y: 200 });

什么时候合并

你发现两个 setState 总是一起调用,说明它们应该合并。

避免冗余状态

能算出来的值,不要存 state:

// 不好:fullName 是冗余的
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState('');

useEffect(() => {
  setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);

// 好:直接计算
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const fullName = firstName + ' ' + lastName;

冗余状态的隐患

冗余 state 容易不同步。firstName 改了,fullName 可能忘了更新。

避免”镜像” props

不要把 props 复制成 state:

// 不好:把 props 复制成 state
function User({ user }) {
  const [name, setName] = useState(user.name);
  // props 变了,state 不会自动更新
}

// 好:直接用 props
function User({ user }) {
  return <span>{user.name}</span>;
}

如果确实需要”可修改的副本”,用 key 重置组件,或者只在初始化时用 props 设 state。

避免矛盾状态

多个 state 可能互相矛盾时,合并成一个:

// 不好:status 和 isSending 可能矛盾
const [isSending, setIsSending] = useState(false);
const [isSent, setIsSent] = useState(false);

// 好:一个状态变量表示所有情况
const [status, setStatus] = useState('typing');
// 'typing' | 'sending' | 'sent'

function handleSend() {
  setStatus('sending');
  // 请求完成后
  setStatus('sent');
}

避免重复状态

同一份数据不要存两份:

// 不好:items 和 itemCount 重复
const [items, setItems] = useState([]);
const [itemCount, setItemCount] = useState(0);

// 好:count 从 items 计算
const [items, setItems] = useState([]);
const itemCount = items.length;

避免过深嵌套

嵌套太深的 state 更新很痛苦:

// 不好:三层嵌套
const [form, setForm] = useState({
  user: {
    profile: {
      address: { city: '' }
    }
  }
});

// 好:拆成多个独立 state
const [city, setCity] = useState('');
const [street, setStreet] = useState('');

嵌套太深怎么办

如果业务确实需要嵌套,考虑用 Immer 库简化更新。后面会讲。

实战:设计一个表单的 state

function CheckoutForm() {
  // 表单字段:合并成一个对象
  const [form, setForm] = useState({
    name: '',
    email: '',
    address: ''
  });

  // 提交状态:一个变量表示所有情况
  const [status, setStatus] = useState('idle');
  // 'idle' | 'submitting' | 'success' | 'error'

  // 错误信息:从 status 派生
  const isSubmitting = status === 'submitting';
  const canSubmit = form.name && form.email && !isSubmitting;

  function updateField(field, value) {
    setForm({ ...form, [field]: value });
  }

  return (
    <form>
      <input
        value={form.name}
        onChange={e => updateField('name', e.target.value)}
        disabled={isSubmitting}
      />
      <button disabled={!canSubmit}>
        {isSubmitting ? '提交中...' : '提交'}
      </button>
    </form>
  );
}

状态结构检查清单

设计 state 时问自己:

  • 这个值能算出来吗?能算就别存
  • 这两个 state 总是一起变吗?合并
  • 这个值和另一个 state 会矛盾吗?合并
  • 这个值是 props 的副本吗?直接用 props
  • 嵌套超过两层了吗?考虑拆分

本节要点

  • 相关状态合并,独立状态拆分
  • 能计算的值不要存 state
  • 一个状态变量表示多种情况,避免矛盾
  • 嵌套太深考虑拆分或 Immer