// App.tsx
'use client'
import { useOptimistic, useRef, useState } from 'react'
import sendMessage from './actions/sendMessage'
type Message = {
text: string
key: number
sending?: boolean
}
export default function App() {
const [messages, setMessages] = useState<Message[]>([])
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state: Message[], newMessage: string) => [
...state,
{ text: newMessage, key: state.length, sending: true },
]
)
const formRef = useRef<HTMLFormElement>(null)
async function formAction(formData: FormData) {
const message = formData.get('message')?.toString()
if (!message) return
addOptimisticMessage(message)
formRef.current?.reset()
try {
const sentMessage = await sendMessage(message)
setMessages((currentMessages) => [
...currentMessages,
{ text: sentMessage, key: messages.length },
])
} catch (error) {
console.error(error)
}
}
return (
<div>
<p>
Typing in message and clicking "Send" will use optimistic update.
<br />
To demonstrate failed operation type in "error" and click send.
</p>
<div>
{optimisticMessages.map((message) => (
<div key={message.key}>
{message.text}
{message.sending && <small> (Sending...)</small>}
</div>
))}
</div>
<form action={formAction} ref={formRef}>
<input type="text" name="message" placeholder="Type a message..." />
<button type="submit">Send</button>
</form>
</div>
)
}
// sendMessage.tsx
export default async function sendMessage(message: string) {
// mock API call
await new Promise((res) => setTimeout(res, 1000))
if (message.toLowerCase().includes('error')) {
throw new Error("Couldn't send message")
}
return message
}