‫So we learned how to
‫fetch and store data in the cache
‫using the "useQuery" hook.
‫But now it's time to learn
‫how to also mutate our remote server state.
‫And so in this lecture,
‫let's learn how we can use the power of
‫React Query to Delete a cabin
‫and automatically re-render the user interface.
‫So first of all,
‫let's come back here to our services.
‫So to this file where we basically
‫interact with the cabin's API.
‫And so here, let's start,
‫by creating a function called "deleteCabin."
‫So this will need to take in the ID
‫of the cabin that we want to Delete,
‫and it's going to be
‫an async
‫function
‫that we want to delete from here.
‫Now, okay?
‫And now just like before,
‫let's actually automatically get our code
‫here from the automatically generated API docs.
‫So I will open this in another tab
‫so that we can keep our table here open at all times.
‫And so here, let's click on "cabins."
‫And then probably somewhere here at the end,
‫we have the code on where to delete rows.
‫And here it is.
‫So let's copy this,
‫paste that here.
‫Or of course if you want,
‫if you prefer,
‫you can also write this here by hand.
‫But anyway, what this does
‫is to again,
‫take our "supabase" client that we created earlier,
‫so this one right here,
‫and then it will select the cabins table
‫and it will delete from there.
‫But of course it shouldn't delete everything.
‫And so we need to tell "supabase" also what to delete.
‫And so here we then basically select the row
‫that we want to delete.
‫And so that row is where the ID column
‫is equal to the ID that we paste in here.
‫And now let's just actually Delete this.
‫So "Cabin could not be deleted."
‫And that's actually it.
‫Now, if we were to attempt to call this function now
‫then actually nothing would happen.
‫And the reason for that is that
‫we activated row level security on this table.
‫So let's go to our row level security policies right here.
‫So in authentication and then policies.
‫And so indeed here,
‫the only rule that we have
‫or the only policy for the cabins
‫is that users are able to select.
‫So basically to read data from the table, but nothing else.
‫So if we want to also enable users to Delete,
‫we need to create a new policy for that.
‫So let's click on New Policy,
‫then let's again use the first one,
‫and so now we will temporarily allow everyone to Delete.
‫However, later on,
‫we will then change this policy
‫to only allow logged in users,
‫so authenticated users,
‫to actually Delete cabins.
‫And the same will be true for Insert, Update and Delete.
‫But now let's select here Delete,
‫instead of Select, which is read.
‫Then here, let's also give this another name.
‫So enable Delete for all users.
‫So make sure that you create this policy
‫because otherwise
‫the function that we created earlier
‫is not going to work.
‫And now before we actually start deleting
‫some rows here,
‫let's just create a few more cabins here.
‫So just really fast
‫because this really doesn't matter here
‫'cause we will just delete them all, anyway, very soon.
‫So I will just create
‫like three more here.
‫You can, of course, use whatever values that you want.
‫This is just so we don't run out of cabins to delete later.
‫So these are luxury cabins,
‫so they are a bit expensive.
‫And yeah, with this we should have enough
‫so we can actually delete them.
‫And first of all,
‫once we come back here now,
‫notice how immediately our page re-fetches the data.
‫So React Query now immediately makes all data here stale.
‫And so that means that whenever we come back to this tab,
‫the re-fetching will happen.
‫So now because of that,
‫we automatically, immediately,
‫got all the cabins that we just created.
‫So this is really, really nice, and really handy.
‫And that's the reason why I said earlier
‫that this really is my favorite React library to work with.
‫And you will find out in this lecture
‫one more reason for that.
‫Which is that now we will use React Query
‫to actually make this Delete button here work.
‫So that Delete button is inside each cabin row.
‫And so let's now use React Query in here
‫to actually make that button work.
‫So the way we do mutations is not by doing "useQuery,"
‫but "useMutation."
‫So just like this.
‫And then here again,
‫we need to paste in an object
‫and the first element should be the "mutationFn."
‫So this is the function that React Query will call,
‫and so let's make this an arrow function,
‫that will receive the ID
‫and will then call the "deleteCabin" function
‫that we just created
‫with exactly that ID.
‫And the way this is going to work
‫will make sense to you
‫when we actually then hook this up
‫with the button there.
‫So for now, just roll with this.
‫And here, of course, it needs to be an actual arrow.
‫All right, and this is actually already enough
‫to make this work.
‫So all we need to do now is to
‫save the results of "useMutation."
‫And here let's just immediately de-structure this.
‫So we will get again the "isLoading" flag
‫and then what we also get is a "mutate" function.
‫And so this is then basically a callback function
‫that we can connect with the button in this case.
‫All right.
‫So here all we have to do now
‫is on the "onClick" prop
‫to call this "mutate" function
‫with the current ID.
‫So let's grab that here as well.
‫So from our prop,
‫and let's rename this to "cabinId"
‫just to make this really explicit.
‫Give this a save,
‫and this should actually already be it.
‫Let's just also use this
‫this "isLoading" state to disable this button.
‫So "isLoading," or maybe this is not the best name.
‫Let's call this here, "isDeleting."
‫Just like this.
‫Now, right.
‫So basically this "mutate" here will be
‫calling this function right here.
‫And actually since we are inputting
‫the same value that we're calling here,
‫we can just make this
‫"deleteCabin" like this.
‫So that's gonna be just the exact same thing.
‫So let's just reload here and then as we click
‫this will then delete the cabin.
‫Okay, so something happened
‫but this is not really that ground breaking yet.
‫Because actually the UI didn't even update.
‫So to make sure that cabin number two
‫has actually been deleted,
‫let's manually reload here.
‫And so indeed cabin number two is gone.
‫So our deletion worked,
‫but again, up until this point,
‫this is nothing so special,
‫But we can make it special,
‫or at least a bit more helpful,
‫by also invalidating the cache
‫as soon as this mutation is done.
‫And so for that
‫we can specify the "onSuccess" callback.
‫So "onSuccess" and this then also accepts a function.
‫And so here we can basically tell
‫React Query what to do
‫as soon as the mutation was successful.
‫So, what do we want to do?
‫Well, we basically want to re-fetch the data here
‫in this situation.
‫And the way this works in React Query,
‫is by invalidating the cache.
‫And here we can actually do that manually.
‫So if I click here,
‫it will then fetch again,
‫and then...
‫And after that, the data becomes stale right away again.
‫But what matters is that
‫as soon as we invalidate this cache,
‫so this data with this key right here,
‫then that will immediately fetch again.
‫Because as the name says,
‫this data is then invalid.
‫Or the Query is invalid.
‫So that's the same thing.
‫So the way that we do this in our code,
‫so of course we don't want our users to click there,
‫is to get the Query client
‫and then call "invalidateQueries" on there.
‫So again,
‫that "invalidateQueries" function
‫actually needs to be called
‫on the "queryClient."
‫So right here.
‫So how do we get access
‫to our "queryClient" instance?
‫Well, for that we have a special hook.
‫So it's a hook which will give us the "queryClient"
‫and it is simply called "useQueryClient."
‫All right?
‫And so now in here we can say
‫"queryClient.invalidateQueries."
‫And then here is where we tell it
‫which exact Query,
‫so which exact data,
‫should be invalidated.
‫So we specify exactly the same "queryKey"
‫that we have there in our dev tools.
‫Or in other words,
‫this one that we defined for this Query.
‫And so this is one of the reasons why it is so important
‫that each Query is uniquely identified.
‫Because then we can now invalidate this Query
‫so that it will fetch again.
‫So that's called "cabins,"
‫and this should now work.
‫So let's see.
‫Let's Delete another one.
‫And this time it looks as though
‫nothing really happened,
‫but it's actually gone.
‫So maybe we have some bug here, but not really.
‫So maybe we just needed to reload here.
‫So let's Delete again,
‫but that didn't work again.
‫But if we invalidate manually
‫then of course that will be gone.
‫So something is wrong.
‫And so let's maybe start by inspecting these "queryKeys."
‫So here this is called "cabin,"
‫and I think I remember,
‫yeah.
‫Here I actually invalidated "cabins."
‫And this "cabins" actually makes a lot more sense
‫because that's also the name in "supabase."
‫So actually our data is called "cabins,"
‫not just "cabin."
‫And so now that reloaded,
‫and we still have this other data here,
‫but now as we reload, then of course that is gone.
‫And now once I Delete this,
‫then, beautiful.
‫Then it automatically disappears from our UI
‫because our data was re-fetched.
‫And now we did run out of cabins to delete.
‫So that's why I told you
‫that we needed to create
‫some here in the beginning.
‫So let's just do two more.
‫Well, not so many.
‫And just another one here.
‫All right, this should be enough.
‫And there they are.
‫And so now,
‫the next thing that I want to quickly show you,
‫so here is that besides the "onSuccess" handler,
‫we also have the "onError" handler.
‫And so this one receives the error that was actually thrown
‫inside this function.
‫So it receives it as an argument,
‫and then here we can do anything we want with this.
‫So let's "alert," in this case, the "err.message."
‫And also let's "alert" something right here.
‫So "Cabin,"
‫and we should make this a string,
‫so "successfully deleted."
‫Well, this is not what I wanted.
‫We want "alert."
‫And so now in order to trigger this error,
‫let's, for example,
‫try to change something here.
‫And so now this is wrong.
‫And so now as we click here on Delete,
‫we will not be able to delete this.
‫So indeed we get the error: "Cabin could not be deleted,"
‫and this is exactly the error message
‫that we throw right here.
‫All right.
‫So here,
‫this "supabase" client will not be able to connect
‫to our API.
‫It will therefore create an error.
‫And so here in case there is this error
‫we then throw a new error with this error message.
‫And so then this is exactly what we get here
‫because...
‫As I was saying,
‫this "onError" event handler
‫gets access to exactly that error.
‫So the error thrown in the mutation function.
‫So let's close this here.
‫And one very interesting thing
‫that we might have noticed...
‫Also down here,
‫is that React Query
‫actually, now of course, also failed to fetch.
‫But we get all of these different errors,
‫and the reason for that...
‫So the reason that we get the error once,
‫then here again, the same error,
‫and then here,
‫once again the same error,
‫is that React Query actually
‫tries to fetch multiple times.
‫Whenever there's an error,
‫React Query will try to
‫fetch the data again a few times.
‫So in order to help us
‫in case that, for example,
‫the error disappeared very quickly,
‫and so then
‫just trying a few times is actually pretty helpful.
‫All right.
‫But now, let's of course,
‫correct this again.
‫Let's reload.
‫And so now as we Delete,
‫not only will the UI automatically re-render,
‫and we also get "Cabin successfully deleted."
‫Great.
‫So this is how mutations work in a nutshell.
‫So we can keep
‫all of the logic that is related to mutations
‫all in one place.
‫So all here in this object
‫inside the "useMutation" hook.
‫And by the end of the section
‫you will also actually abstract this here
‫into its own custom hook,
‫to make this component really clean again.
‫But for now, let's just keep it like this.
‫Now, what I want to do next,
‫is to quickly replace
‫these very ugly alerts right here,
‫with some real world notifications.
‫So just like you see in every web application out there.
‫And so let's make our application here also like that.
‫So a bit more real world.