React 19 example of the useOptimistic() hook
Tags: react, canary
Published: 2024-05-28 21:20:16

---
title: React 19 example of the useOptimistic() hook
published: true
description: a really simple tutorial to get familiar with useOptimistic() hook
tags: react, canary
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cfvvhanpr4lrp39nlnl7.png
series: react
---

## A working example

This post is related to this [example](https://github.com/sensorario/react-typescript-nextjs/tree/next/react/useOptimistic). You can see just the code or read this article that explain step by step what's happening in the little app.

## Create new react app

First of all create new React application. If you like vite, you can use the command below.

```bash
npm create vite@latest
```

## Experimental stuffs

Meanwhile I am writing the hook is still experimental. In the canary version of React 19 so... we need more packages to add the hook to the project.

```bash
npm install react@^0.0.0-experimental-6f23540c7d-20240528 \
react-dom@^0.0.0-experimental-6f23540c7d-20240528 \
uuid@^9.0.1 \
@types/react@18.3 \
@types/react-dom@18.3
```

## Import

The import is obviously...

```TypeScript
import { useOptimistic, useState } from "react";
```

## Data

In this example we will treat books. Because this example will be part of a book I am writing (italian only) about Reacct 19.

```TypeScript
type Book = { text: string; sending: boolean; key?: number };
```

## Rest call

At some point The little example will call some api. For simplicity I am simulating network delay with a Promise. Also, I simulate the the real call will update a value adding " content from rest api" like a rest api is returning data updated/filtered/fixed...

```TypeScript
const createNewBook = async (message: FormDataEntryValue | null) => {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  return message + " content from rest api";
};
```

## App

Here we are!! The application contains a listo of books. React, TypeScript and Next.js is already written. React... is a working progress... The App create a list of books. A function that send data to an api and then add the result to the books.

```TypeScript
export default function App() {
  const [books, setBooks] = useState<Book[]>([
    { text: "React", sending: false, key: 1 },
    { text: "React, TypeScript e Next.js", sending: false, key: 3 },
  ]);

  async function sendMessage(formData: FormData) {
    const sentMessage = await createNewBook(formData.get("message"));
    setBooks((messages: any) => [...messages, { text: sentMessage }]);
  }

  return <Library books={books} createBook={sendMessage} />;
}
```

## Library

The main point of this article is the component `<Library />`. Let's take a step back. The data we are working with are book. And books are stored in this way:

```TypeScript
type Book = { text: string; sending: boolean; key?: number };
```

Whenever sending is true, `<small> (Sending...)</small>` will appear after the new book. And sending is false in books array we have seen before.

```TypeScript
  const [books, setBooks] = useState<Book[]>([
    { text: "React", sending: false, key: 1 },
    { text: "React, TypeScript e Next.js", sending: false, key: 3 },
  ]);
```

We also add a form to add a new title to the book list.

```TypeScript
function Library(/** some params */) {
  /** some contents */

  return (
    <>
      {optimisticContent.map(
        (message: { text: string; sending: boolean }, index: number) => (
          <div key={index}>
            {message.text}
            {!!message.sending && <small> (Sending...)</small>}
          </div>
        )
      )}

      <form action={formAction}>
        <input type="text" name="message" placeholder="Hello!" />
        <button type="submit">Send</button>
      </form>
    </>
  );
}
```

## Form action

Form will send data to `formAction()` function. Then the function add the content to the book list and send form to an external api. (use the imagination about the external api).

```TypeScript
  async function formAction(formData: FormData) {
    addContent(String(formData.get("message")));
    await sendMessage(formData);
  }
```

## useOptimistic

With this syntax we configure `optimisticContent` as optimistic content. For example, when addContent method is called, he receive new book marking the Book with sending=true.

```TypeScript
  const [optimisticContent, addContent] = useOptimistic(
    messages,
    (state: Book[], newMessage: string) => [
      ...state,
      {
        text: newMessage,
        sending: true,
      },
    ]
  );
```

## Complete code

```TypeScript
function Library({
  books: messages,
  createBook: sendMessage,
}: {
  books: Book[];
  createBook: (formData: FormData) => void;
}) {
  async function formAction(formData: FormData) {
    addContent(String(formData.get("message")));
    await sendMessage(formData);
  }

  const [optimisticContent, addContent] = useOptimistic(
    messages,
    (state: Book[], newMessage: string) => [
      ...state,
      {
        text: newMessage,
        sending: true,
      },
    ]
  );

  return (
    <>
      {optimisticContent.map(
        (message: { text: string; sending: boolean }, index: number) => (
          <div key={index}>
            {message.text}
            {!!message.sending && <small> (Sending...)</small>}
          </div>
        )
      )}

      <form action={formAction}>
        <input type="text" name="message" placeholder="Hello!" />
        <button type="submit">Send</button>
      </form>
    </>
  );
}
```

## Resume

When you use a form to send a new Book to the list of books, the `formAction` method add the item into the optimistic list. Then send data to an api, for example. After a while, the api return a value. And then the value is added to the real list of books.