T
TanStack•2y ago
absent-sapphire

v3 -> v5 migration; issues with SSE

While upgrading from react-query v3 to v5, I've hit a roadblock related to custom code that subscribes to an EventSource and updates the queryData in the queryClient on each new event. It looks like this:
const useSSECacheUpdater = <T>(
key: CacheKey,
subscriber: (listener: (data: T) => void) => () => void
): void => {
const queryClient = useQueryClient();

React.useEffect(() => {
return subscriber((data) => {
return queryClient.setQueryData(key, data);
});
}, [key, queryClient, subscriber]);
};
const useSSECacheUpdater = <T>(
key: CacheKey,
subscriber: (listener: (data: T) => void) => () => void
): void => {
const queryClient = useQueryClient();

React.useEffect(() => {
return subscriber((data) => {
return queryClient.setQueryData(key, data);
});
}, [key, queryClient, subscriber]);
};
and gets used like this:
export const useRobotMapQuery = () => {
useSSECacheUpdater(CacheKey.Map, subscribeToMap);
return useQuery(CacheKey.Map, fetchMap, {
staleTime: 1000,
});
};
export const useRobotMapQuery = () => {
useSSECacheUpdater(CacheKey.Map, subscribeToMap);
return useQuery(CacheKey.Map, fetchMap, {
staleTime: 1000,
});
};
That code works fine with v3, however on v5, the SSE connection is torn down and recreated on each new event, because apparently the queryClient dependency of that useEffect changes each time, queryClient.setQueryData is called(?) I'm not 100% sure that that's actually the cause but I can say that the effect function doesn't get called on each new event anymore when I still reference the queryClient in the Effect but don't call the setQueryData method. What do I need to change so that I can recreate that functionality on react-query v5? Alternative question: What would be best practice to integrate server-sent events into React Query v5? For reference, the code in question can be found here: https://github.com/Hypfer/Valetudo/blob/fec428f9c6c10ce59734f80cf5f6b9ee3fb4b617/frontend/src/api/hooks.ts#L245-L256
4 Replies
fair-rose
fair-rose•2y ago
are you sure your queryKeys are arrays? It doesn't like it from the code. CacheKey is just a string ...
absent-sapphire
absent-sapphireOP•2y ago
@TkDodo 🔮 Ah, sorry, yes. They are. The code I've pasted and linked is just the v3 version. For v5 it looks like this:
const useSSECacheUpdater = <T>(
key: Array<QueryKey>,
subscriber: (listener: (data: T) => void) => () => void
): void => {
const queryClient = useQueryClient();

React.useEffect(() => {
return subscriber((data) => {
queryClient.setQueryData<T>(key, (oldData) => {
return data;
}, {
updatedAt: Date.now()
});
});
}, [key, queryClient, subscriber]);
};
const useSSECacheUpdater = <T>(
key: Array<QueryKey>,
subscriber: (listener: (data: T) => void) => () => void
): void => {
const queryClient = useQueryClient();

React.useEffect(() => {
return subscriber((data) => {
queryClient.setQueryData<T>(key, (oldData) => {
return data;
}, {
updatedAt: Date.now()
});
});
}, [key, queryClient, subscriber]);
};
export const useRobotMapQuery = () => {
useSSECacheUpdater([QueryKey.Map], subscribeToMap);

return useQuery({
queryKey: [QueryKey.Map],
queryFn: fetchMap,

staleTime: 1000
});
};
export const useRobotMapQuery = () => {
useSSECacheUpdater([QueryKey.Map], subscribeToMap);

return useQuery({
queryKey: [QueryKey.Map],
queryFn: fetchMap,

staleTime: 1000
});
};
The
(oldData) => {
return data;
}
(oldData) => {
return data;
}
is basically just shotgun debugging and trying if it made a difference to pass a function instead of the value directly. It didn't I've also pushed the v5 version here: https://github.com/Hypfer/Valetudo/blob/0514f5303a50b3a42936db0195ff074ba2bad884/frontend/src/api/hooks.ts#L245-L260 It's essentially the same though. Just with the changes required for migrating from v3 to v5 (unless of course I've missed something and that's the issue 🙃 )
fair-rose
fair-rose•2y ago
It's not. If you pass [QueryKey.Map] to your custom hook and use it as an effect dependency, it be a new array reference every render. RQ doesn't care about that, but useEffect does
absent-sapphire
absent-sapphireOP•2y ago
Dang. You're right Sorry for wasting your time. I was sure that I did already try passing a primitive string key to the function but then again I was also at it for a few hours so most likely I just did something nonsensical there. Thanks!

Did you find this page helpful?