React Query 3迁移后,如何保持getMenu函数参数为string类型?
嘿,我完全懂你现在的困扰——React Query 3.0更新后,useQuery的查询函数参数强制要求用QueryFunctionContext<any, any>类型,但你想保留getMenu原本接受string类型key的写法,不想改动这个函数的定义对吧?别慌,有几个简单可行的解决思路:
思路1:直接从Context中提取字符串类型的queryKey
QueryFunctionContext对象里的queryKey属性,其实就是你传给useQuery的第一个参数(也就是你原来写的menu/${slug}这个字符串)。我们只需要把它取出来,转成string类型传给getMenu就行,完全不用改动原函数:
export const useMenu = (slug: string): UseQueryResult<IMenuPayload, IServerErrorObject> => { return useQuery<IMenuPayload, TError, IMenuPayload>( `menu/${slug}`, // 从Context中解构出queryKey,转成string后传入getMenu ({ queryKey }) => getMenu(queryKey as string) ); };
这个方案最直接,代码改动量最小,完美适配你的场景。
思路2:新增适配层函数(保持原函数纯净)
如果你想完全不改动useMenu里的调用逻辑,或者以后还有其他地方要调用getMenu、不想让它依赖React Query的类型,可以写一个适配函数,把QueryFunctionContext转换成string类型后再调用原getMenu:
// 适配函数:接受QueryFunctionContext,内部处理后调用原getMenu const getMenuAdapter = async (context: QueryFunctionContext): Promise<IMenuPayload> => { const key = context.queryKey as string; return getMenu(key); }; // 在useMenu中使用这个适配函数 export const useMenu = (slug: string): UseQueryResult<IMenuPayload, IServerErrorObject> => { return useQuery<IMenuPayload, TError, IMenuPayload>( `menu/${slug}`, getMenuAdapter ); };
这个方案能让getMenu保持独立,不耦合React Query的类型定义,适合需要复用getMenu的场景。
思路3:改用数组形式queryKey(React Query推荐写法)
如果愿意稍微调整一下queryKey的格式,React Query其实更推荐用数组作为queryKey(方便缓存管理和依赖追踪),这种情况下我们可以从数组中拼接出需要的string key:
export const useMenu = (slug: string): UseQueryResult<IMenuPayload, IServerErrorObject> => { return useQuery<IMenuPayload, TError, IMenuPayload>( ['menu', slug], // 改成数组形式的queryKey ({ queryKey }) => { // 从数组元素拼接出原string格式的key const key = `menu/${queryKey[1]}`; return getMenu(key); } ); };
这个方案虽然调整了queryKey的格式,但更符合React Query的最佳实践,后续缓存相关的操作会更灵活。
为什么会出现这个问题?
React Query 3.0升级后,为了给查询函数提供更多上下文信息(比如缓存状态、分页参数等),把原来直接传入的queryKey(string/数组)改成了包含更多属性的QueryFunctionContext对象,所以原来直接传string的写法就会触发类型报错。我们的核心思路就是从这个Context对象中提取出原本的string类型queryKey,再传递给getMenu,从而维持原函数的参数类型不变。
内容的提问来源于stack exchange,提问作者Amped

