tailwindlabs / headlessui

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

Passing ref to PopoverButton the ref get nullified on rerenders #3413

Open ori-shalom opened 1 month ago

ori-shalom commented 1 month ago

What package within Headless UI are you using? @headlessui/react

What version of that package are you using? v2.1.2

What browser are you using? Chrome

Reproduction URL https://codesandbox.io/p/devbox/affectionate-microservice-87nfcg

Describe your issue When trying to pass a ref to PopoverButton the ref get nullified when the component rerender.

For example given the following code:

import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'
import { createRef, useEffect, useState } from 'react'

export function App() {
  const btnRef = createRef()
  const [counter, setCounter] = useState(0)
  useEffect(() => console.log(btnRef.current), [btnRef])

  return (
    <div className="grid h-screen w-screen place-content-center">
      <button onClick={() => setCounter((c) => c + 1)}>{counter}</button>
      <Popover>
        <PopoverButton ref={btnRef}>Popover Btn</PopoverButton>
        <PopoverPanel anchor={{ gap: 1 }}>Panel</PopoverPanel>
      </Popover>
    </div>
  )
}

Opening the browser console we can see that initially the ref is pointing to the button correctly but once we click on the counter button and the component rerender the console printing shows that the ref is not pointing at null.

Motivation What I'm trying to do is being able to open the popover manually based on other custom events. With the lack of a better way to do it, I thought I would use a ref to the button and call ref.current.click() to toggle the popover state.

A better way for me to accomplish that would be to have an handle ref or something on the Popover itself that exposes an open() function but with the lack of such way to open it programmatically I tried the ref on the button and got blocked by this bug.

Workaround For now I found a workaround which is creating another button inside the PopoverButton and setting the ref on it instead. Something like this:

<PopoverButton as={Fragment}><button ref={btnRef}>Popover Btn</button></PopoverButton>