派生状态模式

多个状态变量从同一个来源推导,而不是各自独立维护,避免多状态错位和手动同步。

#type / concept #status / growing #tech / dev / frontend #resource / react

[!info] related notes

派生状态模式

一句话定义

如果一个状态可以从另一个状态计算出来,它就不应该被单独 useState 维护,而应该作为计算结果直接派生。

核心机制

问题:多个状态需要手动同步

const [currentConversation, setCurrentConversation] = useState(null);
const [chatSessionKey, setChatSessionKey] = useState(() => crypto.randomUUID());

// 新建会话后
setCurrentConversation(newConv);
setChatSessionKey(`conversation:${newConv.id}`);
navigate(`/consultation/${newConv.id}`);

// 删除会话后
setCurrentConversation(null);
setChatSessionKey('new');
navigate('/consultation');

三个地方要同步改,漏一个就错位。

解决:从单一来源派生

const routeConversationId = id && id !== 'new' ? id : null;

// chatSessionKey 从 routeConversationId 派生
const chatSessionKey = routeConversationId
  ? `conversation:${routeConversationId}`
  : 'new';

// currentConversation 从 query data 派生
const currentConversation = conversationQuery.data?.conversation ?? null;

// messages 从 query data 派生
const messages = conversationQuery.data?.messages ?? [];

现在只需要改一个地方(navigate),其他都自动推导。

最小例子

// ❌ 手动同步
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState('');
// 每次改 firstName 或 lastName 都要 setFullName

// ✅ 派生
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const fullName = `${firstName} ${lastName}`;
// fullName 自动跟随变化

适用场景

场景派生方式
当前会话 IDid && id !== 'new' ? id : null
聊天 session keyrouteConversationId ? \conversation:${routeConversationId}` : ‘new’`
当前会话详情conversationQuery.data?.conversation ?? null
消息列表conversationQuery.data?.messages ?? []
提取信息consultationQuery.data?.extracted_info ?? []
诊断阶段consultationQuery.data?.phase ?? 'collecting'
是否加载中conversationQuery.isPending
是否有错误conversationQuery.isError

边界与易混淆点

  • 派生状态不需要 useEffect 来同步,它是同步计算的
  • 如果派生计算很重,用 useMemo 包裹
  • 派生状态和”计算属性”是同一个思路,只是在 React 里需要显式写出来
  • 不是所有状态都该派生:频繁变化的输入 + 昂贵的计算 = 考虑 useMemo;简单赋值 = 直接写
创建于 2026/7/2 更新于 2026/7/15