Ensure TransitionRoot component without props transitions correctly (#3147)
* ensure `TransitionRoot` component without props transitions correctly A bit of a weird one, but you can use the `TransitionRoot` component without any props for transitions themselves (so no real transition can happen). Even crazier, it can happen that it doesn't even render a DOM node, but just its children. At this point, the `TransitionRoot` component is purely there for orchestration purposes of child components. Since there is no DOM node in certain situations, the transitions (and its `onStart` and `onStop` callbacks) won't even happen at all. This causes a bug (obvious in react strict mode) where children don't properly mount or the transition component doesn't properly unmount. * update changelog
This commit is contained in:
@@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Add optional `onClose` callback to `Combobox` component ([#3122](https://github.com/tailwindlabs/headlessui/pull/3122))
|
||||
- Make sure `data-disabled` is available on virtualized options in the `Combobox` component ([#3128](https://github.com/tailwindlabs/headlessui/pull/3128))
|
||||
- Add `overflow: auto` when using the `anchor` prop ([#3138](https://github.com/tailwindlabs/headlessui/pull/3138))
|
||||
- Ensure `TransitionRoot` component without props transitions correctly ([#3147](https://github.com/tailwindlabs/headlessui/pull/3147))
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -559,6 +559,7 @@ function TransitionRootFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_C
|
||||
let [state, setState] = useState(show ? TreeStates.Visible : TreeStates.Hidden)
|
||||
|
||||
let nestingBag = useNesting(() => {
|
||||
if (show) return
|
||||
setState(TreeStates.Hidden)
|
||||
})
|
||||
|
||||
@@ -590,7 +591,7 @@ function TransitionRootFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_C
|
||||
useIsoMorphicEffect(() => {
|
||||
if (show) {
|
||||
setState(TreeStates.Visible)
|
||||
} else if (!hasChildren(nestingBag)) {
|
||||
} else if (!hasChildren(nestingBag) && internalTransitionRef.current !== null) {
|
||||
setState(TreeStates.Hidden)
|
||||
}
|
||||
}, [show, nestingBag])
|
||||
|
||||
@@ -35,23 +35,31 @@ export function useTransition({ container, direction, classes, onStart, onStop }
|
||||
let inFlight = useRef(false)
|
||||
|
||||
useIsoMorphicEffect(() => {
|
||||
let node = container.current
|
||||
if (!node) return // We don't have a DOM node (yet)
|
||||
if (direction === 'idle') return // We don't need to transition
|
||||
if (!mounted.current) return
|
||||
|
||||
onStart.current(direction)
|
||||
|
||||
d.add(
|
||||
transition(node, {
|
||||
direction,
|
||||
classes: classes.current,
|
||||
inFlight,
|
||||
done() {
|
||||
onStop.current(direction)
|
||||
},
|
||||
})
|
||||
)
|
||||
let node = container.current
|
||||
if (!node) {
|
||||
// No node, so let's skip the transition and call the `onStop` callback
|
||||
// immediately because there is no transition to wait for anyway.
|
||||
onStop.current(direction)
|
||||
}
|
||||
|
||||
// We do have a node, let's transition it!
|
||||
else {
|
||||
d.add(
|
||||
transition(node, {
|
||||
direction,
|
||||
classes: classes.current,
|
||||
inFlight,
|
||||
done() {
|
||||
onStop.current(direction)
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return d.dispose
|
||||
}, [direction])
|
||||
|
||||
Reference in New Issue
Block a user