Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: A <Transition.Child /> is used but it is missing a parent <Transition /> or <Transition.Root />. #3316

Closed
spacecat opened this issue Jun 23, 2024 · 7 comments · Fixed by #3331
Assignees

Comments

@spacecat
Copy link

spacecat commented Jun 23, 2024

What package within Headless UI are you using?

@headlessui/react

What version of that package are you using?

"@headlessui/react": "2.1.0",

What browser are you using?

MacOS Sonoma Version 14.4.1 (23E224)
Safari Version 17.4.1 (19618.1.15.11.14)
Chrome Version 126.0.6478.62 (Official Build) (arm64)

Description:

I tried the new Dialog transition APIs but the transitions are not working for me.

I tried the code from here: #3307
here: #3309
and here: https://headlessui.com/react/dialog#adding-transitions

This is what I have in my own project:

<Dialog open={open} onClose={setOpen}>
      <DialogBackdrop transition className="fixed inset-0 bg-black/30 duration-1000 ease-in-out data-[closed]:scale-95 data-[closed]:opacity-0" />
      <div className="fixed inset-0 flex w-screen items-center justify-center p-4">
        <DialogPanel transition className="bg-white duration-300 ease-in-out data-[closed]:scale-95 data-[closed]:opacity-0">
          <p>
            Are you sure you want to deactivate your account? All of your data
            will be permanently removed.
          </p>
          <div className="flex gap-4"></div>
        </DialogPanel>
      </div>
    </Dialog>

And it's giving me the following error:

1 of 4 errors
Next.js (14.2.4)

Unhandled Runtime Error
Error: A <Transition.Child /> is used but it is missing a parent <Transition /> or <Transition.Root />.

Call Stack
Ne
node_modules/@headlessui/react/dist/components/transition/transition.js (1:1408)
Ne
node_modules/@headlessui/react/dist/components/transition/transition.js (1:3326)

I'm probably just missing something?

@zce
Copy link

zce commented Jun 24, 2024

I researched and found that this is caused by <OpenClosedProvider /> nesting.

like:

<Disclosure> {/* <=  OpenClosedProvider in parent context */}
  <Dialog />
</Disclosure>

let inTransitionComponent = usesOpenClosedState !== null

@RobinMalfait
Copy link
Member

Hey!

Can you share a minimal reproduction repo because the code snippet you shared doesn't reproduce it. As @zce mentioned, incorrectly nesting certain components could cause this behavior, if you are putting your <Dialog /> in a <Disclosure /> make sure to either move it inside the <DisclosurePanel /> or moving it outside of the <Disclosure />

@ThenTech
Copy link

ThenTech commented Jun 24, 2024

I tried to do the same after seeing the changes in v2.1.0, and got the same error.
In my case, I had the static prop on <Dialog />, removing that seems to work fine now, event though I do intent to ignore the internally managed open/closed state.

Edit: After some more testing, there is definitely something wrong when using the <Dialog /> component and children with transition, while also using nested dialogs (which also got updated in v2.1.0). Closing a nested dialog now hides all dialogs, also the one that was open when opening the nested one. So for this, I need the static prop, which throws the error above, so then I still need to wrap <Dialog /> in a Transition as before v2.1.0. Then it works again.

@zce
Copy link

zce commented Jun 24, 2024

make sure to either move it inside the <DisclosurePanel /> or moving it outside of the <Disclosure />

This happens when I use the Disclosure component to implement a mobile navigation collapse scenario, and this navigation also contains a search dialog.

As you said, I can find ways to avoid this, but I don't think it's a very good development experience. I'm not sure if there are other built-in components that use transitionchild, if so, I think they will have similar issues.

I'm very sorry, I'm on my phone and can't provide a repro repo at the moment

@spacecat
Copy link
Author

spacecat commented Jun 24, 2024

@RobinMalfait I will try to create a repro repo tomorrow. In the meantime; I'm checking my code and I am indeed nesting the dialog - I did not think about this before. Here is my code structure:

Please note that Dialog, DialogBody, DialogActions in this first code snippet (parent component) come from Catalyst.

import {
  Dialog,
  DialogActions,
  DialogBody,
} from "@/components/catalyst/dialog";
)

I'm using this version of Catalyst - not sure if it's the latest but pretty new:

# Changelog

## 2024-05-31

- Add Next.js demo app ([#1580](https://github.com/tailwindlabs/tailwindui-issues/issues/1580))
- Fix `Avatar` sizing and padding ([#1588](https://github.com/tailwindlabs/tailwindui-issues/issues/1588))

Parent component:

return (
  <Dialog open={isOpen} onClose={() => setIsOpen(false)}>
    <DialogBody>
           <!-- my JSX - just some HTML tags -->
    </DialogBody>
    <DialogActions>
      <button
        onClick={() => setIsOpen(false)}
        type="button"
      >
        Minimize
      </button>
      <button
        onClick={() => {
          setOpen(true);
        }}
        type="button"
      >
        <CompareIcon className="size-4" />
        Compare
      </button>
      <TableDialog
        open={open}
        setOpen={setOpen}
        selectedBrands={selectedBrands}
      />
    </DialogActions>
  </Dialog>
);

and then in my <TableDialog> component I have this:

Child component:

return (
    <Transition show={open}>
      <Dialog className="relative z-[60]" onClose={() => setOpen(false)}>
        <TransitionChild
          as={Fragment}
          enter="transition-opacity ease-linear duration-300"
          enterFrom="opacity-0"
          enterTo="opacity-100"
          leave="transition-opacity ease-linear duration-300"
          leaveFrom="opacity-100"
          leaveTo="opacity-0"
          unmount={false}
        >
          <div className="fixed inset-0 bg-theme-sidebar-right-backdrop/10 backdrop-blur-[2px]" />
        </TransitionChild>
        <div className="fixed inset-0 z-[60] flex items-center justify-center">
          <TransitionChild
            enter="ease-out duration-300"
            enterFrom="opacity-0 translate-y-0 sm:scale-95"
            enterTo="opacity-100 translate-y-0 sm:scale-100"
            leave="ease-in duration-200"
            leaveFrom="opacity-100 translate-y-0 sm:scale-100"
            leaveTo="opacity-0 translate-y-0 sm:scale-95"
          >
            <DialogPanel className="flex h-full w-full flex-col overflow-hidden bg-theme-app-background sm:h-[90vh] sm:w-auto sm:rounded-lg lg:max-w-7xl">
              <!-- my own JSX - regular HTML tags -->
            </DialogPanel>
          </TransitionChild>
        </div>
      </Dialog>
    </Transition>
  );

and then I replaced the <Dialog> in <TableDialog> with what I wrote earlier (which is causing the error):

  <Dialog open={open} onClose={setOpen}>
      <DialogBackdrop transition className="fixed inset-0 bg-black/30 duration-1000 ease-in-out data-[closed]:scale-95 data-[closed]:opacity-0" />
      <div className="fixed inset-0 flex w-screen items-center justify-center p-4">
        <DialogPanel transition className="bg-white duration-300 ease-in-out data-[closed]:scale-95 data-[closed]:opacity-0">
          <p>
            Are you sure you want to deactivate your account? All of your data
            will be permanently removed.
          </p>
          <div className="flex gap-4"></div>
        </DialogPanel>
      </div>
    </Dialog>

I hope this helps debugging/troubleshooting.

@RobinMalfait
Copy link
Member

I figured out a reproduction that seems to reproduce this. So this should be fixed by #3331, and will be available in the next release. If this does not fix your issue after testing, please open a new issue with a minimal reproduction GitHub repo attached.

You can already try it using:

  • npm install @headlessui/react@insiders.

@spacecat
Copy link
Author

Thanks @RobinMalfait. Will try the insiders build asap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants