This is an idea of implementing a toggle button with useOptimistic. useOptimistic is a React experimental hook that allows apps to update UI optimistically while the actual state updates with async operation. You can learn more about it with those links.
useOptimistic - React
Data Fetching: Server Actions and Mutations | Next.js
Here is the main code of my component.
import { useOptimistic, useRef } from 'react'
import { Button } from './ui/button'
import { HeartIcon, HeartFilledIcon } from '@radix-ui/react-icons'
export function FavoriteButton({
isFavorite,
placeId,
}: {
isFavorite: boolean
placeId: string
}) {
async function formAction(formData: FormData) {
addOptimisticFavorite(!isFavorite)
formRef.current?.reset()
const placeId = formData.get('placeId')
await fetch(`/api/favorite`, {
method: 'POST',
body: JSON.stringify({ placeId }),
})
}
const [optimisticIsFavorite, addOptimisticFavorite] = useOptimistic(
isFavorite,
(state) => !state,
)
return (
<form action={formAction} ref={formRef}>
<input type="hidden" value={placeId} name="placeId" />
<Button variant="ghost" type="submit" onClick={handleClick}>
{optimisticIsFavorite ? <HeartFilledIcon /> : <HeartIcon />}
</Button>
</form>
)
}
optimisticFavorite
toggles immediately after users click the button and gets rolled back when the component receives isFavorite
whose value is different from optimisticFavorite
. Otherwise, optimisticFavorite keeps the client-side state.
In my real code, unfortunately, I needed to deal with two different cases to update isFavorite
in my app, which is not ideal, and the code gets a little messy. Please visit the link below if you’re interested.
ai-gourmet-navigator/src/components/favorite-button.tsx at main ·…
That’s it! Thank you for reading :)
Top comments (0)