在Apollo reactive variables存储复杂状态时如何避免不必要的重渲染?
Apollo Client响应式变量复杂状态性能优化最佳实践
处理Apollo Client响应式变量(Reactive Variables)复杂映射状态的性能问题,目前有三个经过社区验证的成熟方案:
方案1:拆分细粒度响应式变量(最推荐ID映射场景)
这是成本最低、性能最好的方案,不需要额外自定义逻辑,完全符合Apollo原生设计:
不要将所有条目存在一个大的响应式变量中,而是为每个ID对应的对象单独创建独立的响应式变量:
// 初始化:每个item对应独立的响应式变量 const itemVars = { item1: makeVar({ text: "Text 1" }), item2: makeVar({ text: "Text 2" }) } // 组件内仅订阅对应ID的响应式变量 const ListItem = (props) => { const item = useReactiveVar(itemVars[props.id]); return <div>{item.text}</div> } // 更新时仅修改对应ID的响应式变量 itemVars.item1({ text: 'New text' })
该方案下更新单个条目时,只有订阅了该ID的组件会触发重渲染,无额外性能开销,且完全保留响应式变量和GraphQL查询集成的能力。
方案2:自定义带selector的订阅Hook(通用场景)
如果状态结构复杂无法拆分细粒度变量,可以自己实现类似Redux useSelector 的自定义Hook,仅订阅状态中需要的部分,代码实现非常轻量:
import { useReactiveVar } from "@apollo/client"; import { isEqual } from "lodash"; import { useEffect, useState, useRef } from "react"; function useReactiveVarSelector(reactiveVar, selector, compareFn = isEqual) { const fullState = useReactiveVar(reactiveVar); const lastSelected = useRef(selector(fullState)); const [selectedState, setSelectedState] = useState(lastSelected.current); useEffect(() => { const newSelected = selector(fullState); if (!compareFn(newSelected, lastSelected.current)) { lastSelected.current = newSelected; setSelectedState(newSelected); } }, [fullState, selector, compareFn]); return selectedState; }
使用方式如下,只有selector返回的内容发生变化时,组件才会重渲染:
const ListItem = (props) => { const item = useReactiveVarSelector(itemsMapVar, state => state[props.id]); return <div>{item.text}</div> }
如果不需要深比较,可以替换为浅比较函数进一步降低性能开销。
方案3:包装组件+React.memo
就是你提到的现有方案,额外补充优化点:如果列表长度超过1000条,可以配合虚拟滚动库减少实际渲染的组件数量,轻量包装组件的渲染开销在绝大多数场景下可以忽略,适合临时快速优化的场景。
不建议切换第三方状态库,上述三个方案都完全保留响应式变量可作为GraphQL查询响应式部分的核心能力,无需额外的状态同步逻辑。
内容的提问来源于stack exchange,提问作者Scott Mikula
相关产品推荐
相关产品推荐

