使用 React 前向引用时如何附加复合组件(forwardRefExoticComponent 上不存在属性)

Aar*_*ron 6 typescript reactjs

我有一个面板组件,可以选择允许用户使用复合组件。该面板有一个包含关闭功能(按钮)的默认标题,我这样做的主要原因是让用户可以选择使用带有自己的按钮的自定义标题以及他们想要的任何其他内容,所以我选择使用复合组件并允许面板组件检查其子组件中是否存在该组件,如果存在,则不会使用默认标头。我已经一切正常,但我似乎无法弄清楚如何清除打字稿错误。

下面是与 Typescript 相关的代码和我收到的错误。我还链接到一个沙箱,其中包含有错误的工作代码。

我尝试了谷歌搜索的大量变体,但在一天之内找不到解决方案。

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

应用程序.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>
  );
}
Run Code Online (Sandbox Code Playgroud)

面板组件:

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;
Run Code Online (Sandbox Code Playgroud)

错误:

在此输入图像描述

jtb*_*des 10

您可以使用 修复此错误Object.assign。这是有效的,因为 Object.assign 的类型为,因此可以推断出assign<T, U>(target: T, source: U): T & U;类型 T 和 U ,并且返回类型成为forwardRef外来组件类型和具有和属性的对象的混合。HeaderContent

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
  }
);
Run Code Online (Sandbox Code Playgroud)


why*_*gee 9

通过在 Panel.tsx 中执行

Panel.Content = PanelContent;
Panel.Header = PanelHeader 
Run Code Online (Sandbox Code Playgroud)

typescript 会抱怨,因为属性 Content 和 Header 是自定义的,并且在基本的forwardRef 类型上不存在。

要修复它,请声明一个扩展forwardRef基本类型的自定义接口并添加自定义属性标题和内容:

interface IPanel
  extends React.ForwardRefExoticComponent<
    PanelProps & React.RefAttributes<HTMLDivElement>
  > {
  Header: typeof PanelHeader;
  Content: typeof PanelContent;
}
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

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

export const Panel = {
  ...forwardRef,
  Header: PanelHeader,
  Content: PanelContent
} as IPanel;
Run Code Online (Sandbox Code Playgroud)

或者...只是这样做export const Panel: any,但这样做会丢失类型检查

从您的 CodeSandbox 中分叉出来的工作 CodeSandbox