Teachnique
      CourseRoadmaps
      Login

      Introduction

      Project SetupCLI

      IntroductionAPP RouterTemplateLoading UIError Handling404 Page

      Linking and Navigating

      Dynamic RoutesRoute groupsParallel RoutesIntercepting Routes

      Route handlersCacheCommon Questions

      Middleware

      CSRSSR & SSGISR

      React Server ComponentsServer-Side Rendering

      SuspenseStreaming

      Server & Client ComponentServer vs. Client ComponentBest Practices

      Server-Side Rendering Strategies

      Data Fetching & Caching & RedirectThird-Party Request Libraries on the Server SideBest Practices

      Request MemoizationData CacheFull Route CacheRouter Cache

      Server Actions IFormServer Actions IIOptimistic Update

      Tailwind CSSCSS-in-JSSass

      Environment VariablesAbsolute Imports& Module Path AliasesRoute Segment Config

      Image & LCPImage IImage IIOptimizing IOptimizing II

      Next FontFont ParameterApply Styles

      Feedback

      Submit request if you have any questions.

      Course
      Optimistic Update

      Next.js Mastery: From Fundamentals to Full-Stack

      Unlock the power of Next.js with this comprehensive course! Starting with the basics, you’ll learn essential skills such as routing, data fetching, and styling. Progress through practical projects, including building your own React Notes app, to gain hands-on experience. Dive into the source code to understand the inner workings and core principles of Next.js. Perfect for developers with a basic knowledge of React and Node.js, this course ensures you’ll be ready to create high-performance full-stack applications efficiently. Join us and master Next.js, backed by industry experts and a supportive learning community.

      Optimistic Updates

      Optimistic updates are a powerful UX strategy where the UI is updated immediately upon a user action, before confirming the success of the underlying data operation. If the operation fails, the UI may be rolled back or adjusted to reflect the failure.

      1. useOptimistic Hook


      
      React's useOptimistic hook is designed to facilitate optimistic updates. The basic usage involves setting an initial state and providing an update function that merges new values into the current state optimistically. Here’s a basic structure:
      import { useOptimistic } from 'react';
      
      function AppContainer() {
      const [optimisticState, addOptimistic] = useOptimistic(
      state,
      (currentState, optimisticValue) => {
      // Merge and return new state with the optimistic value
      return [...currentState, optimisticValue];
      }
      );
      }
      When used in conjunction with Server Actions, it looks like this:
      'use client'
      
      import { useOptimistic } from 'react'
      import { send } from './actions'
      
      export function Thread({ messages }) {
      const [optimisticMessages, addOptimisticMessage] = useOptimistic(
      messages,
      (state, newMessage) => [...state, { message: newMessage }]
      )
      
      return (
      <div>
      {optimisticMessages.map((m, i) => (
      <div key={i}>{m.message}</div>
      ))}
      <form
      action={async (formData) => {
      const message = formData.get('message')
      addOptimisticMessage(message)
      await send(message)
      }}
      >
      <input type="text" name="message" />
      <button type="submit">Send</button>
      </form>
      </div>
      )
      }

      2. Example: Building a ToDo List with Optimistic Updates


      
      To illustrate optimistic updates, let's build a simple ToDo list application using Next.js, useOptimistic, and Server Actions. Here’s how the project is structured:
      app
      └─ form4
      ├─ actions.js
      ├─ form.js
      └─ page.js

      app/form4/page.js

      import { findToDos } from './actions';
      import Form from './form';
      
      export default async function Page() {
      const todos = await findToDos();
      return (
      <div className="max-w-md mx-auto mt-8 p-6 bg-white rounded-lg shadow-md">
      <h1 className="text-2xl font-bold mb-4">To-do List</h1>
      <Form todos={todos} />
      </div>
      );
      }

      app/form4/form.js

      'use client'
      
      import { useOptimistic } from 'react'
      import { useFormState } from 'react-dom'
      import { createToDo } from './actions';
      
      export default function Form({ todos }) {
      const [state, sendFormAction] = useFormState(createToDo, { message: '' });
      
      const [optimistiToDos, addOptimisticTodo] = useOptimistic(
      todos.map((i) => ({ text: i })),
      (state, newTodo) => [
      ...state,
      {
      text: newTodo,
      sending: true
      }
      ]
      );
      
      async function formAction(formData) {
      addOptimisticTodo(formData.get("todo"));
      await sendFormAction(formData);
      }
      
      return (
      <>
      <form action={formAction} className="mb-4">
      <div className="flex space-x-2">
      <input
      type="text"
      name="todo"
      className="flex-grow px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
      placeholder="Input to-do item.."
      />
      <button
      type="submit"
      className="bg-black text-white px-4 py-2 rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
      >
      Add
      </button>
      </div>
      <p aria-live="polite" className="sr-only">
      {state?.message}
      </p>
      </form>
      <ul className="space-y-2">
      {optimistiToDos.map(({ text, sending }, i) => (
      <li key={i} className="bg-gray-100 p-2 rounded-md flex justify-between items-center">
      <span>{text}</span>
      {!!sending && <small className="text-gray-500 italic">Sending...</small>}
      </li>
      ))}
      </ul>
      </>
      );
      }

      app/form4/actions.js

      'use server'
      
      import { revalidatePath } from "next/cache";
      
      const sleep = ms => new Promise(r => setTimeout(r, ms));
      
      let data = ['Reading', 'Writing', 'Meditation']
      
      export async function findToDos() {
      return data;
      }
      
      export async function createToDo(prevState, formData) {
      await sleep(2500);
      const todo = formData.get('todo');
      data.push(todo);
      revalidatePath("/form4");
      return {
      message: `add ${todo} success!`
      };
      }
      
      The interactive effect is as follows:
      The app immediately updates the ToDo list in the UI when a new item is added, showing it as "Sending..." while the Server Action processes the request. If successful, the "Sending..." label is removed.
      

      Handling Common Issues


      
      Dealing with Cookies:
      'use server'
      import { cookies } from 'next/headers'
      export async function exampleAction() {
      // Get cookie
      const value = cookies().get('name')?.value
      // Set cookie
      cookies().set('name', 'Delba')
      // Delete cookie
      cookies().delete('name')
      }
      
      Redirecting After Actions:
      'use server'
      import { redirect } from 'next/navigation'
      import { revalidateTag } from 'next/cache'
      export async function createPost(id) {
      try {
      // ...
      } catch (error) {
      // ...
      }
      revalidateTag('posts') // Update cached posts
      redirect(`/post/${id}`) // Navigate to the new post page
      }

      Conclusion

      Optimistic updates provide a seamless user experience by making the UI immediately responsive to user actions. While powerful, they require careful handling of potential errors and state rollbacks. Using React's useOptimistic hook alongside Server Actions in Next.js enables you to implement optimistic UI updates effectively.