A working example
This post is related to this example. 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.
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.
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...
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.
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...
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.
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:
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.
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.
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).
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.
const [optimisticContent, addContent] = useOptimistic(
messages,
(state: Book[], newMessage: string) => [
...state,
{
text: newMessage,
sending: true,
},
]
);
Complete code
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.
Top comments (0)