第一章练习答案
搜索词、分类和排序适合放进 URL,因为它们需要刷新后恢复并可分享;结果列表、请求状态和错误属于服务端状态;“过滤后有多少条”是派生值,不应单独存储。组件临时打开的筛选面板属于本地 UI 状态。
哪些状态可以保存
持久化只保存可恢复的用户偏好,不保存 access token。下面的纯函数把版本迁移、损坏 JSON 和未知字段分开处理:
js
export function restoreSettings(raw) {
if (raw == null) return { version: 2, theme: 'system', density: 'comfortable' }
try {
const value = JSON.parse(raw)
if (value.version === 1) {
return { version: 2, theme: value.dark ? 'dark' : 'light', density: 'comfortable' }
}
if (value.version !== 2) throw new Error('unsupported version')
if (!['light', 'dark', 'system'].includes(value.theme)) throw new Error('invalid theme')
if (!['compact', 'comfortable'].includes(value.density)) throw new Error('invalid density')
return { version: 2, theme: value.theme, density: value.density }
} catch {
return { version: 2, theme: 'system', density: 'comfortable' }
}
}
console.assert(restoreSettings('{"version":1,"dark":true}').theme === 'dark')
console.assert(restoreSettings('{"version":2,"theme":"bad"}').theme === 'system')测试还要验证登出时删除用户前缀 key,以及切换用户后 query key 包含新的 userId;这两条不是“清空一个全局对象”可以替代的。
先决定谁拥有状态
状态 owner 的判断结果是:输入草稿归表单组件,已提交筛选归 URL,远程列表归 query cache,未提交的派生计数不单独存储。若把 filteredItems 再保存一份,修改筛选后必须同步两份状态,正是练习要求发现的冗余来源。
只接受仍然有效的结果
竞态题的参考断言是“只接受最后一次请求”:为每次请求分配递增 requestId,响应提交前比较它与当前 id;旧响应即使成功也只能被记录为 discarded,不能覆盖 UI。取消请求减少资源消耗,但不能代替提交前的身份判断。
缓存不是全局真相
缓存题必须区分 query identity、freshness 和 UI 状态。改变 userId 或筛选参数时应生成新 key;mutation 成功后失效相关 key,失败则保留旧数据并呈现可重试错误,不能用全局 loading 覆盖不同请求的生命周期。