外观
状态该放在哪一层?
能留在组件里的状态就留在组件里。只有跨层级、而且变化不频繁的数据才放进 Context 或 provide / inject。服务端数据用请求缓存,不要和按钮开关混在同一个全局 store。
原理
本地状态最近:谁渲染谁持有,更新范围最小。两个兄弟要共享时,再把状态抬到最近的共同父组件。
React Context 适合主题、语言、当前用户这种低频数据。Provider 的 value 一变,所有 useContext 的组件都会渲染,中间的 memo 挡不住。高频字段如果和低频字段放在同一个对象里,低频消费者也会被带着渲染。可以拆成多个 Context,或者用带选择器的状态库,让组件只订阅它读到的那一块。
Redux 的模型是单一 store、用 action 描述发生了什么、用 reducer 算出下一个不可变状态。副作用放在 middleware 或 thunk 里,reducer 保持纯函数。Zustand、Pinia 把同样的「可读、可追踪」收成更少的样板。Vue 这边 Pinia 是当前的选择;Vuex 的 mutation 分层在新项目里不再需要。
接口数据会过期、会重试、会被很多页面共用。用 TanStack Query 这类库管理请求缓存、失效和加载状态。全局 store 里只留真正的客户端状态,例如向导走到第几步、侧边栏是否收起。
追问
什么时候引入全局状态库?
出现很多「为了传一个值而存在」的中间组件,或者多块相距很远的 UI 要读写同一份客户端状态时。只为了一个表单,不要上全局库。
Context 性能差,是不是不能用?
低频数据完全适合。痛点是颗粒度:把会每秒变的数据和很少变的数据放进同一个 value。拆开,或者让高频数据走选择器订阅。
易错点
- 前端 store 里的权限标记不能代替接口鉴权。见 权限控制。
- 把接口响应原样塞进全局 store,又没有失效策略,页面会一直显示旧数据。
- 每次渲染都写
value=会制造新对象,Context 消费者每次都更新。把对象用useMemo稳住,或拆开传。