#reactjs #typescript
#reactjs #typescript
Вопрос:
У меня есть компонент панели, который при необходимости позволяет пользователю использовать составные компоненты. Панель имеет заголовок по умолчанию, который содержит функцию закрытия (кнопка), и основная причина, по которой я это сделал, заключалась в том, чтобы предоставить пользователю возможность использовать пользовательский заголовок со своей собственной кнопкой и все остальное, что он хотел бы, поэтому я решил использовать составные компоненты и разрешить компоненту панели проверятьдля существования компонента в его дочерних элементах, и если это так, он не будет использовать заголовок по умолчанию. У меня все работает, но я не могу понять, как очистить ошибку Typescript.
Ниже приведен код, относящийся к Typescript, и ошибка, которую я получаю. Я также связался с изолированной средой с рабочим кодом с ошибками.
Я перепробовал множество вариантов поиска в Google и через несколько дней не смог найти решение.
Песочница: 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>
);
}
Компонент панели:
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 amp; { 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;
Ошибка:
Комментарии:
1. Если вы просто хотите, чтобы ошибка была устранена, сделайте
export const Panel: any
это в Panel.tsx, ошибка в значительной степени понятна, выполнивPanel.Content = PanelContent
иPanelHeader = PanelHeader
typescript жалуется, поскольку свойства Content и Header являются пользовательскими и не существуют для типа forwardRef по умолчанию.
Ответ №1:
Выполнив в Panel.tsx
Panel.Content = PanelContent;
Panel.Header = PanelHeader
typescript жалуется, поскольку свойства Content и Header являются пользовательскими и не существуют для базового типа forwardRef .
Чтобы исправить это, объявите пользовательский интерфейс, который расширяет базовый тип forwardRef, и добавьте свои пользовательские свойства Заголовок и содержимое:
interface IPanel
extends React.ForwardRefExoticComponent<
PanelProps amp; React.RefAttributes<HTMLDivElement>
> {
Header: typeof PanelHeader;
Content: typeof PanelContent;
}
и используйте его следующим образом:
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;
Или … просто сделайте, export const Panel: any
но при этом вы потеряете проверку типа
Рабочий CodeSandbox разветвлен на ваш.
Комментарии:
1. Спасибо. Однако каждая библиотека, которую я использовал в прошлом, не заставляла разработчика запускать эту панель проверки. Заголовок amp;amp; . Я хотел бы найти способ сохранить общие шаблоны, используемые при использовании составных компонентов.
2. @Aaron обновил ответ и поле CodeSandbox, поэтому вам больше не нужно запускать проверку.
3. Спасибо, что нашли время для ответа. Этот ответ был очень полезен.
Ответ №2:
Вы можете исправить эту ошибку, используя Object.assign
. Это работает, потому что Object.assign вводится как assign<T, U>(target: T, source: U): T amp; U;
, поэтому могут быть выведены типы T и U , а возвращаемый тип становится смесью типа экзотического компонента forwardRef и объекта со Header
Content
свойствами и .
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
}
);
Комментарии:
1. Спасибо, что нашли время для ответа. Этот ответ был очень полезен.