使用Apollo Client的useQuery获取数据后React组件未重渲染问题
问题根因
- Apollo Client默认开启了缓存结构化共享策略:当接口返回的data数据与本地缓存中已有的对应数据完全一致时,会复用原有对象的内存引用。而React的useEffect依赖对比是浅比较,检测到data引用没有变化,就不会触发对应回调执行,你的status也就不会更新。
- 冗余状态引入的额外问题:你额外定义的status状态和对应useEffect完全是不必要的,多了一层状态转换反而会引入时序差:当loading结束后data已经存在,但useEffect的回调是在本次渲染提交之后才执行,此时render逻辑里拿到的status还是旧值,就会出现已经拿到data却还是走了重定向逻辑的问题。
解决方案
最优方案:移除冗余状态
直接在渲染逻辑中用useQuery返回的data做判断,不需要额外维护status:
export default function PrivateRoute({ exact, path, component: Component, ...rest }: Props): ReactElement { const { data, error, loading } = useQuery(GET_CURRENT_USER); if (error) console.log(error); if (loading) return <Spinner></Spinner>; return ( <Route {...rest} render={(props) => data?.id ? ( <> <Navbar></Navbar> <Component {...props} /> </> ) : ( <Redirect to={{ pathname: "/login", state: { from: props.location } }} /> ) } /> ); }
如果你确实需要监听data变化做额外操作
如果后续有其他逻辑需要在data更新时执行,不要把整个data对象放到依赖数组里,换成你实际用到的基本类型字段即可:
// 监听具体的id字段,基本类型值变化就能触发useEffect useEffect(() => { data?.id && setStatus(true); }, [data?.id]);
如果需要强制让useQuery在每次请求完成后都返回新的data引用,可以给useQuery添加配置:
const { data, error, loading } = useQuery(GET_CURRENT_USER, { notifyOnNetworkStatusChange: true });
内容的提问来源于stack exchange,提问作者octaviandd
相关产品推荐
相关产品推荐

