diff --git a/src/index.tsx b/src/index.tsx index 68bced35..18671095 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -50,7 +50,9 @@ export interface TriggerRef { // New version will not wrap popup with `rc-trigger-popup-content` when multiple children export interface TriggerProps { - children: React.ReactElement; + children: + | React.ReactElement + | ((info: { open: boolean }) => React.ReactElement); action?: ActionType | ActionType[]; showAction?: ActionType[]; hideAction?: ActionType[]; @@ -261,9 +263,6 @@ export function generateTrigger( } }); - // ========================== Children ========================== - const child = React.Children.only(children); - const originChildProps = child?.props || {}; const cloneProps: Pick< React.HTMLAttributes, | 'onClick' @@ -311,6 +310,17 @@ export function generateTrigger( // Render still use props as first priority const mergedOpen = popupVisible ?? internalOpen; + // ========================== Children ========================== + const child = React.useMemo(() => { + const nextChild = + typeof children === 'function' + ? children({ open: mergedOpen }) + : children; + return React.Children.only(nextChild); + }, [children, mergedOpen]); + + const originChildProps = child?.props || {}; + // We use effect sync here in case `popupVisible` back to `undefined` const setMergedOpen = useEvent((nextOpen: boolean) => { if (openUncontrolled) { diff --git a/tests/basic.test.jsx b/tests/basic.test.jsx index 04d274b3..c42f155d 100644 --- a/tests/basic.test.jsx +++ b/tests/basic.test.jsx @@ -368,6 +368,22 @@ describe('Trigger.Basic', () => { }); }); + describe('children renderProps', () => { + it('should get current open', () => { + const { container } = render( + Hello!} + > + {({ open }) => } + , + ); + + const button = container.querySelector('button'); + expect(button.textContent).toBe('true'); + }); + }); + describe('destroyPopupOnHide', () => { it('defaults to false', () => { const { container } = render(