tailwindlabs / headlessui

Completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS.
https://headlessui.com
MIT License
25.02k stars 1.03k forks source link

Improve UX by freezing `<ComboboxOptions />` component while closing #3304

Closed RobinMalfait closed 1 week ago

RobinMalfait commented 1 week ago

This PR improves the UX when you are closing a Combobox that uses a Transition while closing.

Typically the Combobox component is used with a filtered list based on the value of the ComboboxInput. When the Combobox closes then the onClose will be called. In this function you typically reset the search query state as well:

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
      <ComboboxInput
        aria-label="Assignee"
        displayValue={(person) => person?.name}
        onChange={(event) => setQuery(event.target.value)}
      />
      <ComboboxOptions anchor="bottom" className="empty:hidden">
        {filteredPeople.map((person) => (
          <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100">
            {person.name}
          </ComboboxOption>
        ))}
      </ComboboxOptions>
    </Combobox>
  )
}

Notice that we use setQuery('') in the onClose.

If you are using a transition, then the following things will happen:

  1. The Combobox is closed
  2. The onClose is triggered, the ComboboxOptions are transitioning out
  3. The query is reset
  4. The filteredPeople list is updated (not filtered yet)
  5. The ComboboxOptions re-renders with the "full" list instead of the filtered list.
  6. The Transition completes after this.

This now means that while transitioning out, the ComboboxOptions is re-rendering with a new list of options even though you just selected an option.

This PR now will "freeze" that state so that we show the contents at the time of closing the Combobox.

vercel[bot] commented 1 week ago

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
headlessui-react ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 20, 2024 2:05pm
headlessui-vue ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 20, 2024 2:05pm