Here in this application,
we would provide a better user experience
if we would immediately update this like status
even before it has been processed in the database.
And that's a concept or a pattern
called optimistic updating,
which in the end simply means that we're optimistic
that our update will work, and therefore we wanna show
the new state immediately to the user,
and perform the update behind the scenes.
And then only if that update would fail
or something like this, we wanna roll back the update.
And React and Next.js make that simple and straightforward
because React provides another built-in hook
called useOptimistic, which you can use
to perform such optimistic updates.
And therefore that's a hook we'll use now.
For that, back in this posts.js file
in the components folder,
I wanna update my Posts here,
which I'm outputting optimistically
whenever the data for one of those posts changed.
Now therefore I'll start by calling useOptimistic
from inside this Posts component.
And I'm actually doing this here in the Posts component
because it's actually the entire posts array
that's being fetched from the database for this posts page.
And it's that entire list of posts that, in the end,
got updated therefore.
So here I'm calling useOptimistic,
but in order to call it we must import this hook from React.
So here at the top of the file you should
import { useOptimistic } from 'react'
Like this.
Now with that, we can call useOptimistic here
in the Posts component.
And this hook now takes a couple of inputs
and gives us a couple of outputs.
The first input it takes is the data
with which we wanna start.
So, the data we fetched from the database.
In this case the post array.
That's the first argument you should pass to useOptimistic.
The second argument is a function
that will be called automatically by React in the end,
when you tell it to call it,
and you'll see how that works in just a second,
that will update this posts array optimistically.
So this will be a function
that changes this post array on the client side
until the change has been processed on the server side,
so that we can change it immediately,
and then only sync it back with that server side state
once that server site update has been performed.
For that, this function should then take two arguments,
where the first argument is the old posts states, so to say.
So, for example, the initial array of posts.
And the second argument is some data
that allows us to perform the update.
And it will be up to us to define
which kind of data we expect here.
In my case here, I expect to get the updatedPostId,
so the ID of the post that should switch
from liked to not liked, or vice versa.
And then we need to add our logic for manipulating the posts
to this function body.
Now before we do that, let's talk about the things
we get back from useOptimistic,
because that will be two elements.
It will be an array with two elements, to be precise.
And the first element will be the updated posts array,
the optimistically updated post array,
and therefore I'll name it optimisticPosts.
The second argument will be a function which we can call
to trigger that optimistic update.
So to trigger this function, which we pass
as a second argument to useOptimistic, in the end.
And I'll call this updateOptimisticPosts.
Obviously these names are up to you.
We can then use the optimistic posts, instead of the posts
from the database, in all the places where we used posts.
So for example here in this if check,
and also here for looping through them.
And we'll soon call this function.
For now, we will just assume that it is being called
with the ID of the post for which the user
changed the like status.
And therefore now in this function body,
we wanna update this posts array accordingly.
And I'll start by getting the index of the post
that should be updated, so that should switch
from liked to not liked, or the other way around.
And we can get that by taking a look at the previous posts.
So the old posts array.
And call the findIndex method,
which is a default method built into JavaScript,
which executes a function on every item in that array.
And in that function we should return true or false.
And for that I'll compare the ID of every post
in that array to the updatedPostId here.
And if we got a match, it will give us the index
of that post in the previous posts array,
and that stands stored in this updatedPostIndex constant.
Now it is possible that we don't find such a post,
and therefore I'll check if updatedPostIndex is equal to -1,
which is a value findIndex will return
if it did not find a post for that condition.
And in that case I just wanna return
the unchanged old posts.
But if we make it past the safe check, we did find a post
that should be updated,
and I wanna update this in a immutable way now.
So I'll create an updatedPost, which is a new object
into which I'll copy all the properties of the old post,
which I'll get by accessing my previous posts
for that updatedPostIndex.
So that will give me access to one specific post.
And I'll spread all the properties
of that post into a new object.
Then I'll change that updatedPost,
and set the likes property, which is the number
of likes this post has, which I'm not using here,
but which I still get from the database.
I'll set that to the updatedPost.likes we had.
And then it depends on whether the post was liked before
or not, because I either want to deduct
the value or add a value.
So I'll check if the updatedPost.isLiked is true.
And if that's the case, I wanna deduct the value,
because it was liked and now it's not liked anymore.
So I want to reduce the number of likes.
Otherwise I want to add a like.
And, more importantly for our application here,
I of course also wanna set the isLiked property
for a given post to the opposite of what it was,
with help of the exclamation mark operator, like this.
This will make sure that we switch it from liked
to not liked, or the other way around.
And then I'll create a newPosts array
where I copy all my old posts.
And I wanna overwrite the post at this updated
postIndexPosition with my updated post.
That would be an immutable way of creating a new posts array
with an updated post based on the old data.
And then here in this function,
which we passed to useOptimistic,
we should return that updated posts array.
And React will then make that updated array available
through that optimisticPosts element it returns
in this array which is returned by useOptimistic.
So all we now have to do is make sure
that we call updateOptimisticPosts,
and that we pass the ID of the post that should be updated
to this function in the end,
so that it will be forwarded to this function.
And we can do that by creating a new action here.
And for that I'll add a action function here.
And I'll name it updatePost.
And I expect to get the postId as an argument here,
just as we did in our server action function before.
And in there I'll call updateOptimisticPosts
and forward the postId to this function.
And that's now, of course, the function
provided by that useOptimistic hook.
And thereafter, I wanna call my togglePostLikeStatus
function, and also pass the postId to that.
And in order to use await here, we should use async here.
And by adding async here, we'll also be able
to use this even though I did not add "use server" here.
But Next.js does allow us to also set functions
like this as client actions, therefore if they have
async in front of them.
So now it's this updatePost function that should be passed
to this post component.
So there we, for example, should add an action prop,
though the name of this prop is up to you
because it's your component.
But you should pass updatePost as a value to that prop.
And then, in that component we should accept
that action prop, or whatever you named it,
and use that as a value for this form action.
And you still should bind the post ID
because we still need that.
But with that, we now again got an extra intermediate
form action that will be executed first,
and that will trigger our optimistic update,
and also make sure that we trigger
that server action behind the scenes.
But thanks to this optimistic update,
the UI should update immediately,
and then the server action still does its thing,
and React and Next.js will then sync everything
thereafter once the server action is done.
That's how you can think about that.
So with that, if you save that all,
you'll see we get an error,
that useOptimistic only works in client components.
Well, that's no problem.
We can simply add "use client" at the top here
because the only server action we're using in that file
is defined in a separate file.
So we can add "use client" without problems here
at the top of this file that holds
the post and posts components.
And with that saved, we can reload again,
we can see our posts.
And now if I unlike that, for example,
you see it's immediately unliked.
If I like it, it's immediately liked here.
And we don't have to wait for that server action to finish.
And that's optimistic updating in action.