Documentation - Tags & Invalidation#61
Conversation
| # Migrating from Tanstack | ||
|
|
||
| - no keys, caches function + arguments by default | ||
| - no keys (tags don't require user to adopt naming scheme), caches function + arguments by default |
There was a problem hiding this comment.
I know both these libraries and this is confusing to me. I'd keep it as no keys. Because in tanstack keys are required to even make a query. You don't need to use tags at all. In fact some simple apps I'd expect to not need tags.
There was a problem hiding this comment.
this file is not done, just taking notes along the way
There was a problem hiding this comment.
nit: maybe don't commit unfinished pages or mark things that don't need reviewed. Just so I can skip them
docs/core-concepts/queries.md
Outdated
| } | ||
|
|
||
| const catsQuery = query(searchCats, ['Maine Coon']) | ||
| const catsQuery = query(searchCats, () => ['Maine Coon']) |
There was a problem hiding this comment.
Did we update the query function to accept a getter? I thought last we left it query accepted an array and use query accepted a getter.
There was a problem hiding this comment.
oh duh, yeah I misremembered our change was only for useQuery. Good catch
| # Tags & Invalidation | ||
|
|
||
| <!-- Tags provide a powerful way to organize and invalidate cached queries in Kitbag query. They allow you to group related queries and efficiently update your cache when data changes. | ||
| When it comes time to refresh your query, there are a few options. First, each query has an `execute()` function, which will force the query to refetch and update the reactive `data` for your query but also any other query that shares the same function and arguments. |
There was a problem hiding this comment.
This second sentence is pretty long. Maybe break it up a bit.
Also I'd avoid "fetch" terminology. Actions are just functions. They don't necessarily fetch
| ## Tags | ||
|
|
||
| const { queryClient } = createQueryClient() | ||
| Often we have multiple queries to different functions that are related to each other. For example, any queries that use the current user token in API headers would probably want to be called again if the token changes. Ideally these queries that are otherwise unrelated could be grouped together. |
There was a problem hiding this comment.
I think this could be simpler to understand if it was user id rather than token.
There was a problem hiding this comment.
I chose token and API headers specifically because I thought it was a good example of a thing that could change and is NOT gonna be an argument for the query functions. I feel like userId could be an argument for fetchMe or maybe more so getNotifications in the readers mind
| invalidateTags: [userTag, profileTag] | ||
| } | ||
| ) | ||
| export const requiresUserContext = tag() |
There was a problem hiding this comment.
Yeah this example feels to complex to me. Let's make a cute cat example. We just need to show creating a tag, adding the tag to two queries, and then refreshing by the tag.
There was a problem hiding this comment.
I tried something simpler and cat themed. Let's see what you think of this version
| ## Tag Type | ||
|
|
||
| Invalidate queries based on conditions: | ||
| Kitbag Query also tracks a generic on each tag, which By default is `unknown`. This satisfies any query, but assigning your actual type not only validates the query type but also provides improved type safety later when accessing cached data, like in `setQueryData` |
There was a problem hiding this comment.
"Kitbag Query also supports typed tags. Where a generic type can be added to a tag for extra type safety"
Or something along those lines. We don't tract generics. We support typed tags. (That's like a concept, typed tags)
| const dashboardTag = tag('dashboard') | ||
| const analyticsTag = tag('analytics') | ||
| const settingsTag = tag('settings') | ||
| As an example, let's group some queries that all use the same external API. That way when the API version changes, we can update all the queries at once because they share the same tag. |
There was a problem hiding this comment.
You're going to have to walk me through the thought behind this sort of example. Tags are really intended to be a grouping of related data. So I don't really understand this use case.
There was a problem hiding this comment.
I'd just make the tags catsTag and refresh all cats data. The api version stuff is distracting IMO.
| ## Advanced Invalidation Patterns | ||
| const catNamesQuery = useQuery(randomCatNames, () => [...], { tags: [usesCatsApi] }) | ||
| const catYearsQuery = useQuery(convertToCatYears, () => [...], { tags: [usesCatsApi] }) | ||
| const catYearsQuery = useQuery(checkCatIsNapping, () => [...], { tags: [usesCatsApi] }) |
| ### Background Invalidation | ||
|
|
||
| Invalidate without showing loading states: | ||
| Kitbag Query also supports typed tags. Where a generic type can be added to a tag for extra type safety. By default the tag type is `unknown`. This satisfies any query, but assigning your actual type not only validates the query type but also provides improved type safety later when accessing cached data, like in `setQueryData`. |
There was a problem hiding this comment.
This is how its suppose to work, but be aware its more of a concept then actually working code at this point.
There was a problem hiding this comment.
It works for basic cases, but once you start having multiple types it kinda falls apart.
| ::: code-group | ||
|
|
||
| const userQuery = query('user', fetchUser, { | ||
| tags: [userTag] | ||
| }) | ||
| ```vue [components/CatViewer.vue] | ||
| <script lang="ts" setup> | ||
| import { useQuery } from '@kitbag/query' | ||
|
|
||
| const userFriendsQuery = query('user-friends', fetchUserFriends, { | ||
| tags: [userTag, userRelationsTag] | ||
| }) | ||
| const { catId } = defineProps<{ | ||
| catId: string | ||
| }>() | ||
|
|
||
| const userGroupsQuery = query('user-groups', fetchUserGroups, { | ||
| tags: [userTag, userRelationsTag] | ||
| }) | ||
|
|
||
| // Update user profile - only invalidate direct user data | ||
| userMutation.mutate(userData, { | ||
| invalidateTags: [userTag] | ||
| }) | ||
|
|
||
| // Update friend relationship - invalidate user relations | ||
| addFriendMutation.mutate(friendData, { | ||
| invalidateTags: [userRelationsTag] | ||
| }) | ||
| const query = useQuery(fetchCat, () => [catId]) | ||
| </script> | ||
| ``` | ||
|
|
||
| ### By Access Patterns | ||
|
|
||
| Group by how data is accessed: | ||
|
|
||
| ```ts | ||
| const listTag = tag('list') // List views | ||
| const detailTag = tag('detail') // Detail views | ||
| const searchTag = tag('search') // Search results | ||
|
|
||
| const usersListQuery = query('users-list', fetchUsers, { | ||
| tags: [userTag, listTag] | ||
| }) | ||
|
|
||
| const userDetailQuery = query('user-detail', fetchUserDetail, { | ||
| tags: [userTag, detailTag] | ||
| }) | ||
|
|
||
| const userSearchQuery = query('user-search', searchUsers, { | ||
| tags: [userTag, searchTag] | ||
| }) | ||
| ``` | ||
| ::: |
There was a problem hiding this comment.
I'd skip this example, we already went over how to create queries. Just show a typed tag example.
| ``` | ||
|
|
||
| ### 2. Don't Over-Tag | ||
| ```vue [components/CatViewer.vue] |
There was a problem hiding this comment.
The two parts example isn't necessary IMO. We can just create a typed tag, create a tagged query with that tag, then call refreshData with the tag with a specific value. And show that it'll refresh the query only if the id matches.
| # Migrating from Tanstack | ||
|
|
||
| - no keys, caches function + arguments by default | ||
| - no keys (tags don't require user to adopt naming scheme), caches function + arguments by default |
There was a problem hiding this comment.
nit: maybe don't commit unfinished pages or mark things that don't need reviewed. Just so I can skip them
Co-authored-by: Craig Harshbarger <pleek91@gmail.com>
craig-pplx
left a comment
There was a problem hiding this comment.
Couple last suggestions. Otherwise very clean and concise. Love it
| } | ||
| ) | ||
| // invalidates only queries that call searchCats with ['Himalayan'] | ||
| refreshQueryData(searchCats, () => ['Himalayan']) |
There was a problem hiding this comment.
I don't think this uses a getter. It's not reactive.
| search: tag('search') | ||
| function save(catId: string): Promise<void> { | ||
| ... | ||
| refreshQueryData(catIdTag(catId)) |
There was a problem hiding this comment.
Along with this we should show how on a query the tags can be a getter. So the list of cats and be tagged based on each cat in the list. That way if cat 1 is refreshed, the list of cats (which contains cat 1) is refreshed as well.
Co-authored-by: Craig Harshbarger <craig.harshbarger@perplexity.ai>
Continue working through missing documentation pages
side quest: