Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to attach a compound component when using React forward ref (property does not exist on forwardRefExoticComponent)

I have a Panel component that optionally allows the user to use compound components. The panel has a default header that contains the close functionality (button) and the main reason I did this is was to give the user the option to use a custom header with their own button and whatever else they would like, so I opted to use compound components and to allow the Panel component to check for the existence of the component in its children and if so, it will not use the default header. I have it all working but I can't seem to figure out how to get the Typescript error to clear.

Below is the code pertaining to Typescript and the error I'm getting. I also linked to a sandbox with the working code with errors.

I have tried tons of variations from Google searches and in a days time cannot find the solution.

Sandbox: https://codesandbox.io/s/panel-513yq?file=/src/Panel/Panel.tsx:510-4061

App.js:

export default function App() {
  const [open, setOpen] = React.useState(false);

  const handleClose = () => {
    setOpen(false);
  };

  const handleOpen = () => {
    setOpen(true);
  };

  return (
    <div id="page" data-test-id="component-app">
      <header id="header">
        <button onClick={handleOpen}>Open</button>
      </header>
      <main id="main">
        <Panel open={open} onClosed={handleClose} renderPortal={true}>
          <Panel.Header>
            <div>
              <button onClick={handleClose}>Close</button>
            </div>
          </Panel.Header>
          <Panel.Content>Panel Content</Panel.Content>
        </Panel>
      </main>
    </div>
  );
}

Panel Component:

interface ModalPropsComposition {
  Header?: React.FC<PanelHeaderProps>;
  Content?: React.FC<PanelContentProps>;
}

export interface PanelProps
  extends React.HTMLAttributes<HTMLDivElement>,
    ModalPropsComposition {
  open: boolean;
  position?: string;
  renderPortal?: boolean;

  // Lifecycle callbacks
  onClose?: () => void;
  onClosed?: () => void;
  onOpen?: () => void;
  onOpened?: () => void;
}

/**
 * @Codeannex UI React: Panel Component
 *
 * Panel Component
 */
const _Panel = ({
  children,
  open,
  renderPortal,
  onClose,
  onClosed,
  onOpen,
  onOpened
}: PanelProps & { forwardedRef: React.Ref<HTMLDivElement> }): JSX.Element => {
  // panel code...
};

export const Panel = React.forwardRef(
  (props: PanelProps, ref: React.Ref<HTMLDivElement>): JSX.Element => {
    return <_Panel {...props} forwardedRef={ref} data-testid={PANEL_TEST_ID} />;
  }
);

Panel.Header = PanelHeader;
Panel.Content = PanelContent;

Error:

enter image description here

like image 396
Aaron Avatar asked Aug 25 '26 14:08

Aaron


1 Answers

You can fix this error by using Object.assign. This works because Object.assign is typed as assign<T, U>(target: T, source: U): T & U;, so the types T and U can be inferred, and the return type becomes a mix of the forwardRef exotic component type and the object with Header and Content properties.

export const Panel = Object.assign(
  React.forwardRef(
    (props: PanelProps, ref: React.Ref<HTMLDivElement>): JSX.Element => {
      return (
        <PanelComponent
          {...props}
          forwardedRef={ref}
          data-testid={PANEL_TEST_ID}
        />
      );
    }
  ),
  {
    Header: PanelHeader,
    Content: PanelContent
  }
);
like image 196
jtbandes Avatar answered Aug 27 '26 05:08

jtbandes



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!