tailwindlabs / headlessui

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

Maximum update depth exceeded" when rendering <MenuButton> as Fragment #3476

Closed mohammadhosseinmoradi closed 2 months ago

mohammadhosseinmoradi commented 2 months ago

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

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

What browser are you using? Chrome

Describe your issue This issue appears to be triggered when the MenuButton is wrapped in a Fragment.

Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
<Menu>
  <MenuButton as={Fragment}>
    <button>Menu</button>
  </MenuButton>
</Menu>
RobinMalfait commented 2 months ago

Hey!

This should be fixed by #3478, and is available in the latest release (2.1.8).

You can already try it using:

mrlubos commented 1 month ago

@RobinMalfait there's another edge case I found related to this I believe, here's a video https://youtu.be/wVMtqbnkF4o

Notice when I open and close the menu, everything is fine. When it's open however, it expands the scrollable area. If I scroll to this "new" area and then close the menu, the error occurs. This is an edge case because it happens only when the scroll area expands as far as I can tell. This issue has existed for a long time too, it's not specific to 2.1.8.

SPannan commented 5 days ago

This "feature" still exists in the @latest (2.2.0). Rendering Menu Items as fragment still results in "maximum update length". If I roll back from 2.2.0 to 1.7.19, error goes away.

When I "click" menu item button re-render throws error.

`import { Menu, Transition } from "@headlessui/react"; import { Fragment } from "react"; import { EllipsisVerticalIcon, TrashIcon } from "@heroicons/react/24/solid";

export default function MessageOptionsDropdown({ message }) {

return (
    <div className="absolute right-full text-gray-100 top-1/2 -translate-y-1/2 z-10">
        <Menu as="div" className="relative inline-block text-left">
            <div>
                <Menu.Button className="flex justify-center items-center w-8 h-8 rounded-full hover:bg-black/40">
                    <EllipsisVerticalIcon className="h-5 w-5" />
                </Menu.Button>
            </div>
            <Transition
                as={Fragment}
                enter="transition ease-out duration-100"
                enterFrom="transform opacity-0 scale-95"
                enterTo="transform opacity-100 scale-100"
                leave="transition ease-in duration-75"
                leaveFrom="transform opacity-100 scale-100"
                leaveTo="transform opacity-0 scale-95"
            >
                <Menu.Items className="absolute left-0 mt-2 w-48 rounded-md bg-gray-800 shadow-lg z-[100]">
                    <div className="px-1 py-1 ">
                        <Menu.Item>
                            {({ active }) => (
                                <button
                                    className={`${
                                        active
                                            ? "bg-black/30 text-white"
                                            : "text-gray-100"
                                    } group flex w-full items-center rounded-md px-2 py-2 text-sm`}
                                >
                                    <TrashIcon className="w-4 h-4 mr-2" />
                                    Delete
                                </button>
                            )}
                        </Menu.Item>
                    </div>
                </Menu.Items>
            </Transition>
        </Menu>
    </div>
);

} `