Implementing ReactJS new useOptimistic Hook and Next.js 13.4 Server Actions for Optimistic UI.

Implementing ReactJS new useOptimistic Hook and Next.js 13.4 Server Actions for Optimistic UI.

·

4 min read

The useOptimistic hook provides a way to implement optimistic updates in your application. These Optimistic updates enhance the user experience by making the app appear more responsive to the user by displaying instant results on the front end. Even if the server takes a moment to respond, you can update the page right away to show changes. In this article, we'll learn how to use the "useOptimistic" hook in React.js along with Next.js 13.4 server action to do this. We'll create an example of a "Like Counter" one using the regular method and one using the optimistic method. This way, we can see the difference and learn how optimistic updates improve the user experience.

Implementing the Optimistic Counter

Let's take a closer look at how to implement the optimistic counter using the "useOptimistic" hook. Here are the steps involved:

Before we begin, make sure you have the following installed:

  • Node.js (with npm or yarn) and a code editor of your choice.

  • Latest Next.Js installed in your project.

Server Action for Like Counter

First, To enable the like counter functionality within your Next.js 13 application, a dedicated server action, manageLikes has been created. This function handles the like counter. The amount parameter, denoting the number of likes to be added, is sent in the request body and we are going to increment the likes in the backend. Here we use the revalidateTag to make sure the frontend updates.

// ../actions/manageLikes.ts

"use server";

import { revalidateTag } from "next/cache";


export const manageLikes = async (amount: number) => {
    if (!amount) {
        return
    }

    await fetch("http://localhost:3000/likes", {
        method: "POST",
        cache: "no-cache",
        body: JSON.stringify({amount}),
        headers: {
            "Content-Type": "application/json",
        }
    })
    revalidateTag("likes")
}

Backend API for Managing Likes

A GET request has been configured to retrieve the current like count. When this endpoint is accessed, the server responds with a JSON representation of the current like count. Then a POST request is used to receive the amount value through the request. Upon successful retrieval of the amount, the like count is incremented by the provided value. The updated like count is then returned as a JSON response.

// ../likes/route.ts

import { NextResponse } from "next/server";

// Initialize default like count
let likes = 50

export async function GET() {
    return NextResponse.json({ likes })
}

export async function POST(request: Request) {
    const { amount } = await request.json()

    if (amount) {
        likes += Number(amount)
    }

    return NextResponse.json({
        likes
    })

}

Implementation of the Simple Like Counter

We create a simple client-side component named Counter which imports our server action and added two buttons to manage likes.

// ../components/Counter.tsx

"use client";

import { manageLikes } from "../actions/manageLikes";

type Props = {
  likes: number;
};

function Counter({ likes }: Props) {
  return (
    <div>
      <h2>Normal Counter</h2>
      <button onClick={() => manageLikes(-1)}>-</button>
      <span> {likes} </span>
      <button onClick={() => manageLikes(1)}>+</button>
    </div>
  );
}

export default Counter;

Implementing the Optimistic Like Counter

As of now, the UseOptimistic hook is still an experimental feature so I have imported it like this otherwise you can import it accordingly. here the function updateLikes which is triggered when clicking on any button, instead of directly calling the server action we call this new function and instead of showing the likes we have used the likes to initialize the optimisticLike and we have rendered the optimisticLike.

// ../components/OptimisticCounter.tsx

"use client";

import { manageLikes } from "../actions/manageLikes";
import { experimental_useOptimistic as UseOptimistic } from "react";

type Props = {
  likes: number;
};

function OptimisticCounter({ likes }: Props) {
  const [optimisticLike, addOptimisticLike] = UseOptimistic(
    likes,
    (state, amount) => state + Number(amount)
  );

  const updateLikes = async (amount: number) => {
    addOptimisticLike(amount);
    await manageLikes(amount);
  };

  return (
    <div>
      <h2>Optimistic Counter</h2>
      <button onClick={() => updateLikes(-1)}>-</button>
      <span> {optimisticLike} </span>
      <button onClick={() => updateLikes(1)}>+</button>
    </div>
  );
}
export default OptimisticCounter;

Adding to the Frontend for Demonstration

To showcase the like counter functionality on the front end, modifications have been applied to thepages.tsx file.

import Counter from "./components/Counter";
import OptimisticCounter from "./components/OptimisticCounter";

export default async function Home() {
  const res = await fetch("http://localhost:3000/likes", {
    cache: "no-cache",
    next: {
      tags: ["likes"],
    },
  });

  const { likes } = await res.json();

  return (
    <main>
      <h1>UseOptimistic Demo Counter</h1>

      <Counter likes={likes} />

      <OptimisticCounter likes={likes} />
    </main>
  );
}

Conclusion

In this article, we explored how to create optimistic updates in the UI using ReactJs new useOptimistic hook and Next.js 13.4 Server Actions. this is how Instagram has immediate feedback, what you see in the day-to-day usage of the app is most likely optimistic UI updates

Here, we looked at how a "Like" button could work. Instead of waiting for the server to confirm alike, we can show it instantly on the UI and then sync it with the server.

Optimistic updates are handy for many cases where we want apps to feel snappy. But remember, sometimes the UI might show something that's not exactly what's on the server. If things go wrong, we might need to fix errors or undo the optimistic update.

By using the new useOptimistic hook in React.js and Next.js's server action, you can create applications that respond quickly and smoothly, giving users a better experience.