2022
(he/him)
{...design}
{...design}
✨
click
mutation event
mutation request
response
update content
woohoo!
Don’t
go
chasing
waterfalls
💭 this song is from 1995
Mutations
Optimistic
Optimistic
click
mutation event
mutation request
response
update content
woohoo!
✅
click
mutation event
mutation request
💥
update content
woohoo!
update content
Pardon. retry?
if your App/API fails often, you probably have a bug
not a silver bullet
🐛
🔫
when the response is not critical for the user experience
when you trust the availability of the resource
🐶
☀️
when a page loads
`GET`
requests
⬇️
⚙️
when a data mutates
`POST`
requests
⬆️
⚙️
`PUT`
`DELETE`
`PATCH`
`useLoaderData()`
`useActionData()`
GET form submission
POST / PUT / PATCH / DELETE form submission
from remix
not react
⚠
navigation
idle
idle
loading
idle
idle
submitting
idle
idle
submitting
loading
1️⃣
2️⃣
3️⃣
const PageComponent = () => {
const initialData = useLoaderData()
const transition = useTransition()
return (
<h1>
{transition.submission
? `Submitted ${transition.submission.formData.get('username')}`
: `Already have ${initialData?.username || 'nothing'}`
}
</h1>
)
}
This hook lets you plug your UI into your actions and loaders without navigating
-- from Remix docs
POST
PUT
DELETE
PATCH
Action
Function
Loader
Function
<Form>
<fetcher.Form>
vs
<form>
vs
🪝
const PageComponent = () => {
const { data, submission, state } = useFetcher()
const initialData = useLoaderData()
const formData = submission.formData
const food = fetcher.data
? `${data.cuisine} ${data.dish}`
: initialData.food
return (
<h1>
{state === 'submitting'
? `We are eating ${formData.get('dish')} for dinner`
: `We are eating ${food || 'nothing'} for dinner`
}
</h1>
}
So, are Optimistic Mutations a
thing?
No.
Tanstack Query
featuring
getServerSideProps
QueryClient
initialData
Hydrate page with data
cache query
mutate
✅
❌
Re-Render page w/ new data
update cache
Re-Render page w/ old data
pull from cache
warn user
function App({ Component, pageProps }) {
const [queryClient] = useState(
() => new QueryClient()
)
return (
<QueryClientProvider client={queryClient}>
{/*... app stuff goes here */}
</QueryClientProvider>
)
}
function Page({ pageData }) {
const { data } = useQuery(
['data'],
() => {
return getData(user)
},
{
initialData: pageData,
}
)
return <Component />
}
const getServerSideProps = async ({
req,
res
}) => {
const user = await getUser()
return {
props: {
pageData: await fetchData(user)
},
}
}
const useAddTodo = (user) => {
const queryClient = useQueryClient()
return useMutation(
(newTodo: TodoProps) => addTodo(newTodo, userEmail), {
onMutate: async (newTodo) => {
await queryClient.cancelQueries(['todos'])
const previousTodos = queryClient.getQueryData(['todos'])
queryClient.setQueryData(['todos'], (oldTodos => [
newTodo,
...oldTodos,
])
return { previousTodos }
},
onError: (err, _newTodo, context) => {
queryClient.setQueryData(['todos'], context.previousTodos)
},
onSettled: () => {
queryClient.invalidateQueries(['todos'])
},
})
)
}