2022
(he/him)
{...design}
{...design}
β¨
Server
renders
static
data source
Browser
renders
all
all
click
mutation event
mutation request
response
update content
woohoo!
click
mutation event
mutation request
response
update content
woohoo!
Donβt
go
chasing
waterfalls
π this song is from 1995
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 to 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 loaderData = useLoaderData()
const transition = useTransition()
return (
<h1>
{transition.state === 'submitting'
? `Submitted ${transition.submission.formData.get('username')}`
: `Already have ${loaderdata?.username || 'nothing'}`
}
</h1>
)
}
<Form>
<fetcher.Form>
vs
<form>
vs
πͺ
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
const PageComponent = () => {
const { data, submission, state } = useFetcher()
const loaderdata = useLoaderData()
const formData = submission.formData
const food = fetcher.data
? `${data.cuisine} ${data.dish}`
: loaderdata.food
return (
<h1>
{state === 'submitting'
? `We are eating ${formData.get('dish')} for dinner`
: `We are eating ${food || 'nothing'} for dinner`
}
</h1>
}
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'])
},
})
)
}