DEV Community

Cover image for React Query: How to organize your keys

React Query: How to organize your keys

Red Ochsenbein (he/him) on August 01, 2022

If you're using React Query you certainly know how the useQuery hook works. Some example similar to the ones you find in the React Query documentat...
Collapse
 
jackmellis profile image
Jack

Best thing to do is to create specific functions for your queries:

export const useTodo = (id: string) => {
  return useQuery(['todo', id], () => fetchTheTodos(id));
}
Enter fullscreen mode Exit fullscreen mode
const { data: todos } = useTodo(props.id);
Enter fullscreen mode Exit fullscreen mode

and so on.

Hooks are composable so make the most of it.

Collapse
 
syeo66 profile image
Red Ochsenbein (he/him) • Edited

This will not help you when trying to invalidate the query. Of course you could create a invalidate function/hook for each query, too. But then the pattern starts to look not much different from what I'm proposing.

Or you can even use a combination of both: Create custom hooks PLUS use the key/query objects.

Collapse
 
jackmellis profile image
Jack

Invalidation is a whole other problem for sure. I usually have an invalidate hook that invalidates all queries within a certain domain i.e.

const invalidate = useInvalidateBasketQueries();

return useQuery(key, fn, { onSuccess: invalidate });
Enter fullscreen mode Exit fullscreen mode

I've been using react query (and vue query) since day 1 and it's important we get some good/consistent patterns in place

Collapse
 
ivan_jrmc profile image
Ivan Jeremic

Invalidating is easy why not just,

const queryClient = useQueryClient();

queryClient.invalidateQueries(["key"]);
Enter fullscreen mode Exit fullscreen mode

Works for me with normal queries and custom hooks.

Thread Thread
 
syeo66 profile image
Red Ochsenbein (he/him)

Yes sure. But how would make sure that keys stay consistent in your mid-size to large React application? What if the key used by the query at another place of the application (by another person)? How do you make sure the invalidation also gets updated?
This is what my pattern tries to solve.

Thread Thread
 
ivan_jrmc profile image
Ivan Jeremic

I don't understand what you mean by "invalidation also gets updated" what you mean by updated? Or do you mean other devs on your team will accidentally use an already existing key for a different query?

Collapse
 
dikamilo profile image
dikamilo

I prefer to create a separate key map per API namespace. It's looks like this:

export const myzoneKeys = {
  all: ['myzone'] as const,

  profile: (profileId: string | undefined) =>
    [...myzoneKeys.all, profileId || 'no-profile'] as const,

  myList: (profileId: string | undefined) =>
    [...myzoneKeys.profile(profileId), 'my-list'] as const,

  recommendations: (profileId: string | undefined) =>
    [...myzoneKeys.profile(profileId), 'recommendations'] as const,

  recommendationsDetail: (profileId: string | undefined) =>
    [...myzoneKeys.profile(profileId), 'recommendations-detail'] as const,

  recentlyWatched: (profileId: string | undefined) =>
    [...myzoneKeys.profile(profileId), 'recently-watched'] as const,

  recentlyWatchedChannel: (profileId: string | undefined) =>
    [...myzoneKeys.recentlyWatched(profileId), 'channel'] as const,

  recentlyWatchedProgram: (profileId: string | undefined) =>
    [...myzoneKeys.recentlyWatched(profileId), 'program'] as const,

  recentlyWatchedRecording: (profileId: string | undefined) =>
    [...myzoneKeys.recentlyWatched(profileId), 'recording'] as const,

  recentlyWatchedVod: (profileId: string | undefined) =>
    [...myzoneKeys.recentlyWatched(profileId), 'vod'] as const,
};
Enter fullscreen mode Exit fullscreen mode

In complex apps, this have several advantages:

  • I can invalidate whole namespace (since in this case is per user profile) using all key
  • I can invalidate all recently watched queries using recentlyWatched key, since all other recently watched keys are based on this
  • I can invalidate single query be specific key name
  • It's constant and easy to use and understand in react query devtools

Also, I don't use useQuery across the app. I create custom query hooks in single API packages divided to API namespace and use it in app.

Collapse
 
syeo66 profile image
Red Ochsenbein (he/him)

I really like this approach. Thanks for sharing.

Collapse
 
brauliodiez profile image
Braulio Diez

I think this post explain the concept mentioned by @dikamilo tkdodo.eu/blog/effective-react-que...

Collapse
 
vkostunica profile image
vkostunica

this topic requires much deeper elaboration than this

Collapse
 
syeo66 profile image
Red Ochsenbein (he/him)

Isn't that the truth for 99% of all topics in coding?