ycs77 / headlessui-float

Easily use Headless UI with Floating UI to position floating elements.
https://headlessui-float.vercel.app
MIT License
344 stars 11 forks source link

Visible flicker when selecting an option in Listbox wrapped in Float #61

Closed dd-jonas closed 10 months ago

dd-jonas commented 1 year ago

Use Version Use version when bugs appear:

Describe the bug When selecting a Listbox option that is wrapped in Float, there seems to be an extra render cycle before the options window closes. This is especially visible in my case as I render a check icon in front of the selected option, which means that this icon can be quickly seen appearing before the options close, causing a visual flash.

To Reproduce https://stackblitz.com/edit/react-ts-dbrsvf?file=App.tsx

Left Listbox uses Float, right one doesn't. Only the left one has the visible flash mentioned before. If it happens too fast, it can be recorded using the performance tab in chrome devtools.

Expected behavior No visible flash / immediately closing the options on select.

ycs77 commented 10 months ago

Hi @dd-jonas, I'm not seeing the flickering issue that you're describing when I view the reproduction example. I also don't see the issue in the Chrome DevTools performance tab.

Would you be able to record a GIF animation of the bug?

dd-jonas commented 10 months ago

Hmm strange, here's a video and slowed down GIF of what's happening. You can see the checkmark appear after click, and then the menu closing. On the other hand, the dropdown without flicker closes immediately on click, which I would expect here as well.

https://github.com/ycs77/headlessui-float/assets/69415794/c2529029-926b-460f-8946-181f55fa064a

16x slowed down: trimmed-1dvz8n1u_RQo8HXUL

ycs77 commented 10 months ago

I have investigated the issue and found that it is caused by the <Transition> component. You can see an example of the issue in the following code:

import { Listbox, Transition } from '@headlessui/react'
import * as React from 'react'
import '../style.css'

const people = [
  'Wade Cooper',
  'Arlene Mccoy',
  'Devon Webb',
  'Tom Cook',
  'Tanya Fox',
  'Hellen Schmidt',
]

export default function ExampleCombobox() {
  const [value1, setValue1] = React.useState(null)

  return (
    <div className="wrapper">
      <div>
        <label>With flicker</label>

        <Listbox value={value1} onChange={setValue1} as="div">
          <Listbox.Button>{value1 ?? 'Select'}</Listbox.Button>
          {/* the flicker is from `<Transition>` */}
          <Transition as={React.Fragment}>
            <Listbox.Options>
              {people.map(name => (
                <Listbox.Option key={name} value={name} className="option">
                  <span className="check">✓ </span>
                  {name}
                </Listbox.Option>
              ))}
            </Listbox.Options>
          </Transition>
        </Listbox>
      </div>
    </div>
  )
}

Headless UI Float uses the <Transition> component to determine whether the Headless UI components are open or not. Therefore, I am unable to fix the issue at the moment. You may need to report the issue to Headless UI.

If you are very concerned about the issue, you can use Floating UI instead. Floating UI provides more customization options than Headless UI Float. Headless UI Float is designed to be a simple way to position Headless UI components and is therefore unable to address this issue.