W
Wasp-lang•3mo ago
JakeLoew

How to update data coming from `useQuery`

Let's say you have the following code in an app that has a model Author as well as model Book where each book belongs to one author...
import { getAuthor, createBook } from 'wasp/client/operations'

function MyComponent({ authorId }: { authorId: string; }) {
const { data: author } = useQuery(getAuthor, { id: authorId }) // populates author with books
const createNewBookByAuthor = async (authorId: number) => {
await createBook({ title: "new book!", authorId });
}

return <div>
<button onClick={createNewBookByAuthor}>Create New Book</button>
<ul>
{author.books.map(book => {
<li>
{book.title}
</li>
})}
</ul>
</div>
}
import { getAuthor, createBook } from 'wasp/client/operations'

function MyComponent({ authorId }: { authorId: string; }) {
const { data: author } = useQuery(getAuthor, { id: authorId }) // populates author with books
const createNewBookByAuthor = async (authorId: number) => {
await createBook({ title: "new book!", authorId });
}

return <div>
<button onClick={createNewBookByAuthor}>Create New Book</button>
<ul>
{author.books.map(book => {
<li>
{book.title}
</li>
})}
</ul>
</div>
}
What is a good pattern to update data coming from the getAuthor query when a new book is created?
6 Replies
kapa.ai
kapa.ai•3mo ago
Hi @JakeLoew. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- In Wasp, you can use the useAction hook to enhance your actions with additional features such as optimistic updates. This allows you to update the cache immediately after an action is performed, without waiting for the server response. In your case, you can use the useAction hook to enhance the createBook action. You would need to define an optimistic update that updates the cache of the getAuthor query when a new book is created. Here is an example of how you can do this:
import { useQuery, useAction, getAuthor, createBook } from 'wasp/client/operations'

function MyComponent({ authorId }: { authorId: string; }) {
const { data: author } = useQuery(getAuthor, { id: authorId }) // populates author with books
const createNewBookByAuthor = useAction(createBook, {
optimisticUpdates: [
{
getQuerySpecifier: ({ authorId }) => [getAuthor, { id: authorId }],
updateQuery: (newBook, oldData) => ({ ...oldData, books: [...oldData.books, newBook] }),
},
],
})

return <div>
<button onClick={() => createNewBookByAuthor({ title: "new book!", authorId })}>Create New Book</button>
<ul>
{author.books.map(book => {
<li>
{book.title}
</li>
})}
</ul>
</div>
}
import { useQuery, useAction, getAuthor, createBook } from 'wasp/client/operations'

function MyComponent({ authorId }: { authorId: string; }) {
const { data: author } = useQuery(getAuthor, { id: authorId }) // populates author with books
const createNewBookByAuthor = useAction(createBook, {
optimisticUpdates: [
{
getQuerySpecifier: ({ authorId }) => [getAuthor, { id: authorId }],
updateQuery: (newBook, oldData) => ({ ...oldData, books: [...oldData.books, newBook] }),
},
],
})

return <div>
<button onClick={() => createNewBookByAuthor({ title: "new book!", authorId })}>Create New Book</button>
<ul>
{author.books.map(book => {
<li>
{book.title}
</li>
})}
</ul>
</div>
}
In this example, getQuerySpecifier is a function that returns the query specifier for the getAuthor query. updateQuery is a function that returns the new state of the cache. It adds the new book to the list of books of the author. Please note that the updateQuery function must be a pure function and should not perform any side effects. It should work correctly regardless of the state of oldData. For more information, you can refer to the Wasp documentation on the useAction hook and optimistic updates (https://wasp-lang.dev/docs/data-model/operations/actions#the-useaction-hook-and-optimistic-updates). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @JakeLoew, 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?
JakeLoew
JakeLoewOP•3mo ago
It seems that updateQuery doesn't return the result of createNewBook--only the arguments given to the action. I will need the id of book as part of the data set up the optimistic update. Is there no way to handle a failure?
miho
miho•3mo ago
@sodic is the person to talk to about optimistic updates
Filip
Filip•3mo ago
Hey Jake, nice to see you again! Kapa sent you down the completely wrong track. From what I got, you don't need optimistic updates. I can still answer those questions if you want (I see two questions: "can I somehow get the id during the optimistic update" and "is there no way to handle a failure"), but let's first focus on the original problem. So, if I understood correctly, you're adding a book that belongs to the author and you want to invalidate the getAuthor Query. Luckily, this is a textbook use case for Wasp operations. Just add the Author entity to the createBook action and Wasp should take care of everything automatically:
action createBook {
...
entities: [Author, Book]
}
action createBook {
...
entities: [Author, Book]
}
Let me know if this took care of it. If not, we have a bug and I need to fix it ASAP 😅 We explain it in more detail here: https://wasp-lang.dev/docs/data-model/operations/actions#cache-invalidation
JakeLoew
JakeLoewOP•3mo ago
You're exactly right about what I was looking for. Thanks so much! So queries in wasp update when actions update entities specified within that query. Perfect for my use case. Thanks!
Filip
Filip•3mo ago
No problem! Well phrased questions are easy to answer. Keep it up 😄
Want results from more Discord servers?
Add your server