How to refresh table data after deleting a record in Wasp?

Hey! I'm using Wasp with a table UI that lists records from a query. When I delete a record via a button in the table, the record is removed from the database, but the table UI doesn't update automatically. What’s the recommended way to ensure the table reflects the updated data after deletion? I'm using the default query (e.g. getTasks) and deleteTask action. Should I manually invalidate the query with React Query's queryClient.invalidateQueries, or is there a more idiomatic way to handle this in Wasp? Perhaps there is something wrong with the entities I defined in main.wasp?
10 Replies
kapa.ai
kapa.ai2w ago
Hi @infelipe Gomes. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- In Wasp, your table should automatically update after deleting a record, as Wasp provides automatic query invalidation based on entity relationships. Let me explain how this works and how to troubleshoot your issue. Automatic Query Invalidation in Wasp Wasp automatically invalidates query caches when actions that use the same entity are executed. This means that if your deleteTask action and getTasks query both use the Task entity, executing deleteTask should invalidate the getTasks cache, causing it to refetch data and update your UI. As mentioned in the Wasp documentation:
When you declared the getTasks and createTask operations, you specified that they both use the Task entity. So when createTask is called, Wasp knows that the data getTasks fetches may have changed and automatically updates it in the background. This means that out of the box, Wasp keeps all your queries in sync with any changes made through Actions.
Wasp Tutorial: Actions Troubleshooting If your table isn't updating automatically, here are some things to check: 1. Entity Declaration: Make sure both your query and action properly declare the same entity. For example:
query getTasks {
fn: import { getTasks } from "@src/queries",
entities: [Task]
}

action deleteTask {
fn: import { deleteTask } from "@src/actions",
entities: [Task]
}
query getTasks {
fn: import { getTasks } from "@src/queries",
entities: [Task]
}

action deleteTask {
fn: import { deleteTask } from "@src/actions",
entities: [Task]
}
2. Using useQuery: Ensure you're using the useQuery hook from Wasp to fetch your data, not calling the query function directly. The useQuery hook makes your component reactive:
import { useQuery } from '@wasp/queries'
import { getTasks } from '@wasp/queries/getTasks'

// In your component:
const { data: tasks, isLoading, error } = useQuery(getTasks)
import { useQuery } from '@wasp/queries'
import { getTasks } from '@wasp/queries/getTasks'

// In your component:
const { data: tasks, isLoading, error } = useQuery(getTasks)
Wasp Tutorial: Queries Manual Invalidation (if needed) If automatic invalidation isn't working for some reason, you can manually invalidate queries. While Wasp doesn't currently have a native API for this (beyond optimistic updates with useAction), you can use react-query's mechanisms directly:
If you wish to optimistically set cache values after performing an Action, you can do so using optimistic updates. Configure them using Wasp's useAction hook. This is currently the only manual cache invalidation mechanism Wasps supports natively. For everything else, you can always rely on react-query.
Wasp Docs: Cache Invalidation Next Steps If you've verified all the above and still have issues: 1. Double-check your entity relationships in main.wasp 2. Ensure your action is successfully completing (add console logs to confirm) 3. Check if you're using any custom caching or state management that might interfere with Wasp's automatic invalidation The Wasp team is actively working on improving cache invalidation mechanisms, as tracked in GitHub issue #63, but the current entity-based system should work for most common cases like your table deletion scenario. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @infelipe Gomes, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
infelipe Gomes
infelipe GomesOP2w ago
@kapa.ai How can I use react-query to manually invalidate a query?
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- To manually invalidate a query in Wasp using react-query, you can use the configureQueryClient function that Wasp provides. This allows you to access react-query's invalidation capabilities. Here's how you can manually invalidate a query: 1. First, you need to configure the query client in your client setup function:
import { configureQueryClient } from 'wasp/client/operations'

export default async function mySetupFunction() {
// Store the queryClient reference for later use
const queryClient = configureQueryClient({
// Your configuration options here if needed
})

// Make queryClient globally available
window.queryClient = queryClient
}
import { configureQueryClient } from 'wasp/client/operations'

export default async function mySetupFunction() {
// Store the queryClient reference for later use
const queryClient = configureQueryClient({
// Your configuration options here if needed
})

// Make queryClient globally available
window.queryClient = queryClient
}
2. Then, you can invalidate queries when needed:
// To invalidate a specific query
window.queryClient.invalidateQueries(['queryKey'])

// Or in a component after an action
const handleDelete = async (id) => {
await deleteItem(id)
// Manually invalidate the query
window.queryClient.invalidateQueries(['items'])
}
// To invalidate a specific query
window.queryClient.invalidateQueries(['queryKey'])

// Or in a component after an action
const handleDelete = async (id) => {
await deleteItem(id)
// Manually invalidate the query
window.queryClient.invalidateQueries(['items'])
}
While Wasp provides automatic entity-based query invalidation, manual invalidation can be useful in more complex scenarios. Currently, Wasp doesn't have a native API specifically for manual cache invalidation (beyond optimistic updates with useAction), so you need to rely directly on react-query's mechanisms as shown above. The Wasp team is actively working on improving cache invalidation, including RTK-style cache invalidation via operations, as tracked in GitHub issues #547 and #63. For more advanced use cases, you might also want to explore optimistic updates, which Wasp supports through the useAction hook. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @infelipe Gomes, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
miho
miho7d ago
@sodic you might be best suited to answer this I don't think we expose the queryClient for you to manually invalidate the cache based on the docs and the public facing API
miho
miho7d ago
Linked to this thread in the issue where we track this: https://github.com/wasp-lang/wasp/issues/63#issuecomment-2812609536
GitHub
Query invalidation/updating · Issue #63 · wasp-lang/wasp
This comment reflects current state of the issue. Check comments below for discussion. There are three stages, and we plan to implement them in order, from basic towards most advanced: Implement &q...
infelipe Gomes
infelipe GomesOP7d ago
Hi! I managed to solve this problem by importing queryClientInitialized (from queryClientInitialized) and using it like this: (await queryClientInitialized).invalidateQueries(getTasks.queryCacheKey) It worked, but I rather rely on the entities array defined in my main.wasp file. The problem, however, is that they aren't working as expected, at least for me - which is why I'm here 😛 See, I'm working on a scaffolder to generate a admin dashboard for me based on my schema. I was testing it with something simple (the "to do" example in the docs) and that's how I found this issue. Essentially, I have 4 pages (index, new, show, edit), and my index page has a table with an actions column that allows the user to delete a record. The table isn't updated after the record is successfully deleted unless the page is manually refreshed (which is expected, of course). I'm not sure using queryClientInitialized is ideal, safe, recommended, or intentional (was it supposed to be exported?), but it solves the issue regardless
MEE6
MEE67d ago
Wohooo @infelipe Gomes, you just became a Waspeteer level 2!
infelipe Gomes
infelipe GomesOP7d ago
Let me know your thoughts on this 🙂
Filip
Filip2d ago
Hey @infelipe Gomes, you shouldn't need to call in the queryClient for this. It's a textbook case of Wasp's cache invalidation and should be working out of the box with entities (as you guessed). Perhaps we have a bug, or perhaps your code is a little off. Do you mind sharing it? E.g., how do you define those queries and actions in the Wasp file?
infelipe Gomes
infelipe GomesOP2d ago
Hi, @Filip! Sure, I've defined them like so:
// #region Category queries
query getCategory {
fn: import { getCategory } from "@src/operations/category/queries",
entities: [Category]
}

query getCategories {
fn: import { getCategories } from "@src/operations/category/queries",
entities: [Category]
}
// #endregion

// #region Category actions
action createCategory {
fn: import { createCategory } from "@src/operations/category/actions",
entities: [Category]
}

action updateCategory {
fn: import { updateCategory } from "@src/operations/category/actions",
entities: [Category]
}

action deleteCategory {
fn: import { deleteCategory } from "@src/operations/category/actions",
entities: [Category]
}

action deleteCategories {
fn: import { deleteCategories } from "@src/operations/category/actions",
entities: [Category]
}
// #endregion
// #region Category queries
query getCategory {
fn: import { getCategory } from "@src/operations/category/queries",
entities: [Category]
}

query getCategories {
fn: import { getCategories } from "@src/operations/category/queries",
entities: [Category]
}
// #endregion

// #region Category actions
action createCategory {
fn: import { createCategory } from "@src/operations/category/actions",
entities: [Category]
}

action updateCategory {
fn: import { updateCategory } from "@src/operations/category/actions",
entities: [Category]
}

action deleteCategory {
fn: import { deleteCategory } from "@src/operations/category/actions",
entities: [Category]
}

action deleteCategories {
fn: import { deleteCategories } from "@src/operations/category/actions",
entities: [Category]
}
// #endregion

Did you find this page helpful?