Fix ref stealing from children (#1820)
* fix ref stealing When a higher-level component (like `Transition`) provides a `ref` to its child component, then it will override the `ref` that was potentially already on the child. This will make sure that these are merged together correctly. Fixes: #985 * update changelog
This commit is contained in:
@@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Fix `Transition` component's incorrect cleanup and order of events ([#1803](https://github.com/tailwindlabs/headlessui/pull/1803))
|
||||
- Ensure enter transitions work when using `unmount={false}` ([#1811](https://github.com/tailwindlabs/headlessui/pull/1811))
|
||||
- Improve accessibility when announcing `Listbox.Option` and `Combobox.Option` components ([#1812](https://github.com/tailwindlabs/headlessui/pull/1812))
|
||||
- Fix `ref` stealing from children ([#1820](https://github.com/tailwindlabs/headlessui/pull/1820))
|
||||
|
||||
## [1.6.6] - 2022-07-07
|
||||
|
||||
|
||||
@@ -6,6 +6,29 @@ import { Transition } from './transition'
|
||||
|
||||
import { executeTimeline } from '../../test-utils/execute-timeline'
|
||||
|
||||
function nextFrame() {
|
||||
return new Promise<void>((resolve) => {
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
it('should not steal the ref from the child', async () => {
|
||||
let fn = jest.fn()
|
||||
render(
|
||||
<Transition show={true} as={Fragment}>
|
||||
<div ref={fn}>...</div>
|
||||
</Transition>
|
||||
)
|
||||
|
||||
await nextFrame()
|
||||
|
||||
expect(fn).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should render without crashing', () => {
|
||||
render(
|
||||
<Transition show={true}>
|
||||
|
||||
@@ -175,7 +175,8 @@ function _render<TTag extends ElementType, TSlot>(
|
||||
// Filter out undefined values so that they don't override the existing values
|
||||
mergeProps(resolvedChildren.props, compact(omit(rest, ['ref']))),
|
||||
dataAttributes,
|
||||
refRelatedProps
|
||||
refRelatedProps,
|
||||
mergeRefs((resolvedChildren as any).ref, refRelatedProps.ref)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -193,6 +194,20 @@ function _render<TTag extends ElementType, TSlot>(
|
||||
)
|
||||
}
|
||||
|
||||
function mergeRefs(...refs: any[]) {
|
||||
return {
|
||||
ref: refs.every((ref) => ref == null)
|
||||
? undefined
|
||||
: (value: any) => {
|
||||
for (let ref of refs) {
|
||||
if (ref == null) continue
|
||||
if (typeof ref === 'function') ref(value)
|
||||
else ref.current = value
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function mergeProps(...listOfProps: Props<any, any>[]) {
|
||||
if (listOfProps.length === 0) return {}
|
||||
if (listOfProps.length === 1) return listOfProps[0]
|
||||
|
||||
Reference in New Issue
Block a user