joh*_*pin 9 javascript typescript reactjs
官方 ReactJs 文档建议按照点符号创建组件,如React-bootstrap库:
<Card>
<Card.Body>
<Card.Title>Card Title</Card.Title>
<Card.Text>
Some quick example text to build on the card title and make up the bulk of
the card's content.
</Card.Text>
</Card.Body>
</Card>
Run Code Online (Sandbox Code Playgroud)
感谢这个问题,我知道我可以使用功能组件来创建这个结构,就像在 javascript 中一样:
const Card = ({ children }) => <>{children}</>
const Body = () => <>Body</>
Card.Body = Body
export default Card
Run Code Online (Sandbox Code Playgroud)
使用 TypeScript,我决定向其中添加相应的类型:
const Card: React.FunctionComponent = ({ children }): JSX.Element => <>{children}</>
const Body: React.FunctionComponent = (): JSX.Element => <>Body</>
Card.Body = Body // <- Error: Property 'Body' does not exist on type 'FunctionComponent<{}>'
export default Card
Run Code Online (Sandbox Code Playgroud)
现在的问题是 TypeScript 不允许分配Card.Body = Body并给我错误:
类型“FunctionComponent<{}>”上不存在属性“Body”
那么如何正确键入以使用此代码结构呢?
rit*_*taj 13
const Card: React.FunctionComponent & { Body: React.FunctionComponent } = ({ children }): JSX.Element => <>{children}</>
const Body: React.FunctionComponent = (): JSX.Element => <>Body</>
Card.Body = Body;
Run Code Online (Sandbox Code Playgroud)
或更易读:
type BodyComponent = React.FunctionComponent;
type CardComponent = React.FunctionComponent & { Body: BodyComponent };
const Card: CardComponent = ({ children }): JSX.Element => <>{children}</>;
const Body: BodyComponent = (): JSX.Element => <>Body</>;
Card.Body = Body;
Run Code Online (Sandbox Code Playgroud)
我找到了一种巧妙的方法Object.assign来使点表示法与 ts 一起使用。有类似的用例
type TableCompositionType = {
Head: TableHeadComponentType;
Body: TableBodyComponentType;
Row: TableRowComponentType;
Column: TableColumnComponentType;
};
type TableType = TableComponentType & TableCompositionType;
export const Table: TableType = TableComponent;
Table.Head = TableHeadComponent;
Table.Body = TableBodyComponent;
Table.Row = TableRowComponent;
Table.Column = TableColumnComponent;
Run Code Online (Sandbox Code Playgroud)
其中 ts 会抛出错误。我的基本工作解决方案是:
export const Table: TableType = Object.assign(TableComponent, {
Head: TableHeadComponent,
Body: TableBodyComponent,
Row: TableRowComponent,
Column: TableColumnComponent,
});
Run Code Online (Sandbox Code Playgroud)
唯一的缺点是,虽然结果将被类型检查,但对象参数内的各个子组件不会被检查,这可能有助于调试。
一个好的做法是预先定义(和类型检查)参数。
const tableComposition: TableCompositionType = {
Head: TableHeadComponent,
Body: TableBodyComponent,
Row: TableRowComponent,
Column: TableColumnComponent,
};
export const Table: TableType = Object.assign(TableComponent, tableComposition);
Run Code Online (Sandbox Code Playgroud)
但由于Object.assign是通用的,因此这也是有效的:
export const Table = Object.assign<TableComponentType, TableCompositionType>(TableComponent, {
Head: TableHeadComponent,
Body: TableBodyComponent,
Row: TableRowComponent,
Column: TableColumnComponent,
});
Run Code Online (Sandbox Code Playgroud)
当然,如果您不需要(或不想)事先显式指定类型,您也可以这样做,并且只会推断出类型。不需要令人讨厌的黑客攻击。
export const Table = Object.assign(TableComponent, {
Head: TableHeadComponent,
Body: TableBodyComponent,
Row: TableRowComponent,
Column: TableColumnComponent,
});
Run Code Online (Sandbox Code Playgroud)
在花了很多时间弄清楚如何在forwardRef组件中使用点表示法之后,这是我的实现:
卡体组件:
export const CardBody = forwardRef<HTMLDivElement, CardBodyProps>(({ children, ...rest }, ref) => (
<div {...rest} ref={ref}>
{children}
</div>
));
//Not necessary if Bonus feature wont be implemented
CardBody.displayName = "CardBody";
Run Code Online (Sandbox Code Playgroud)
卡组件:
interface CardComponent extends React.ForwardRefExoticComponent<CardProps & React.RefAttributes<HTMLDivElement>> {
Body: React.ForwardRefExoticComponent<CardBodyProps & React.RefAttributes<HTMLDivElement>>;
}
const Card = forwardRef<HTMLDivElement, CardProps>(({ children, ...rest }, ref) => (
<div {...rest} ref={ref}>
{children}
</div>
)) as CardComponent;
Card.Body = CardBody;
export default Card;
Run Code Online (Sandbox Code Playgroud)
在你的代码中使用它看起来像这样:
<Card ref={cardRef}>
<Card.Body ref={bodyRef}>
Some random body text
</Card.Body>
</Card>
Run Code Online (Sandbox Code Playgroud)
如果您想要特定订单:
...CardComponentInterface
const Card = forwardRef<HTMLDivElement, CardProps>(({ children, ...rest }, ref) => {
const body: JSX.Element[] = React.Children.map(children, (child: JSX.Element) =>
child.type?.displayName === "CardBody" ? child : null
);
return(
<div {...rest} ref={ref}>
{body}
</div>
)
}) as CardComponent;
...Export CardComponent
Run Code Online (Sandbox Code Playgroud)
!!! 如果children不存在,则在尝试添加除 CardBody 组件之外的其他任何内容时将会收到错误消息。这个用例非常具体,尽管有时可能很有用。
您当然可以继续添加组件(页眉、页脚、图像等)
| 归档时间: |
|
| 查看次数: |
3150 次 |
| 最近记录: |