TSX
Public
Optimistic Todo List with useOptimistic
Show a new todo immediately while the asynchronous save operation completes in the background.
#react
#hooks
#state
#optimistic-ui
TSX
'use client';
import { startTransition, useOptimistic, useState } from 'react';
type Todo = {
id: string;
text: string;
pending?: boolean;
};
type Props = {
initialTodos: Todo[];
createTodo: (text: string) => Promise<Todo>;
};
export function TodoList({ initialTodos, createTodo }: Props) {
const [todos, setTodos] = useState(initialTodos);
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(currentTodos, text: string) => [
...currentTodos,
{
id: crypto.randomUUID(),
text,
pending: true,
},
],
);
function handleSubmit(formData: FormData) {
const text = String(formData.get('text') ?? '').trim();
if (!text) return;
startTransition(async () => {
addOptimisticTodo(text);
const savedTodo = await createTodo(text);
setTodos((current) => [...current, savedTodo]);
});
}
return (
<>
<form action={handleSubmit}>
<input name="text" placeholder="Add a todo" required />
<button type="submit">Add</button>
</form>
<ul>
{optimisticTodos.map((todo) => (
<li key={todo.id} aria-busy={todo.pending}>
{todo.text}
{todo.pending ? ' (saving…)' : ''}
</li>
))}
</ul>
</>
);
}