Optimistic UX

2022
(he/him)

User Interface

User Experience

Usability

{...design}
{...design}

UI

UX

Usability

✨

Multi-Page

Single-Page

Hybrid

Server

renders

static

data source

Browser

renders

all

all

Traditional Multi-Page Apps

click
mutation event
mutation request
response
update content
woohoo!

Age of Single Page Applications

click
mutation event
mutation request
response
update content
woohoo!

Don’t

go

chasing

waterfalls

💭 this song is from 1995

Hybrid Apps with Optimistic

click
mutation event
mutation request
response
update content
woohoo!
✅

“Expect...

...the unexpected”

those 1 every 999

click
mutation event
mutation request
💥
update content
woohoo!
update content
Pardon. retry?

state chart

important notes

if your App/API fails often, you probably have a bug

not a silver bullet

🐛

🔫

then

when?

when the response is not critical to the user experience

when you trust the availability of the resource

🐶

☀️

loader function

when a page loads

`GET`

requests

⬇️

⚙️

action function

when a data mutates

`POST`

requests

⬆️

⚙️

`PUT`
`DELETE`
`PATCH`
`useLoaderData()`
`useActionData()`

useTransition()

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>
   )
 }

routes/page.jsx

demo

time

<Form>
<fetcher.Form>

vs

<form>

vs

HTML5 Logo

🪝

useFetcher()

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>
 }

routes/page.jsx

demo

time

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>
  )
 }

pages/app.jsx

function Page({ pageData }) {
  const { data } = useQuery(
    ['data'],
    () => {
      return getData(user)
    },
    {
      initialData: pageData,
    }
  )

  return <Component />
}

pages/page.jsx

const getServerSideProps = async ({
  req,
  res
}) => {
  const user = await getUser()
 
  return {
    props: {
      pageData: await fetchData(user)
    },
  }
}

pages/page.jsx

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'])
      },
    })
  )
}

hooks/query.jsx

demo

time

thanks

being optimistic is cool

Optimistic UX

By Atila

Optimistic UX

Remix is a framework revitalises web development by putting progressive enhancement in the front seat. It creates solutions that approximates developers to the platform. It uses Developer Experience as means to enforce web best practices and with that brought the term Optimistic UI back to our daily lives. Let's have a look at what features the Remix offers out-of-the-box to get developers creating high-quality web apps easily and deploying them anywhere that can run JavaScript.

  • 169