[英]How can I pass down an array of objects as a prop to a component and then pass down the array's members as props to nested components?
[英]How can I implement a "as" prop with TypeScript while passing down the props?
我正在構建一個組件庫,我需要其中一些組件具有可自定義的標簽名稱。 例如,有時看起來像<button>
的東西實際上是<a>
。 所以我希望能夠像這樣使用按鈕組件:
<Button onClick={onClick}>Click me!</Button>
<Button as="a" href="/some-url">Click me!</Button>
理想情況下,我希望根據“as”道具推斷可用道具:
// Throws an error because the default value of "as" is "button",
// which doesn't accept the "href" attribute.
<Button href="/some-url">Click me!<Button>
我們可能還需要傳遞一個自定義組件:
// Doesn't throw an error because RouterLink has a "to" prop
<Button as={RouterLink} to="/">Click me!</Button>
這是實現,沒有 TypeScript:
function Button({ as = "button", children, ...props }) {
return React.createElement(as, props, children);
}
那么,如何在傳遞道具時使用 TypeScript 實現“as”道具?
注意:我基本上是在嘗試做styled-components
所做的事情。 但是我們使用的是 CSS 模塊和 SCSS,所以我無法負擔添加樣式組件。 不過,我對更簡單的選擇持開放態度。
我花了一些時間研究樣式化組件的類型聲明。 我能夠提取最低要求的代碼,這里是:
import * as React from "react";
import { Link } from "react-router-dom";
type CustomComponentProps<
C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
O extends object
> = React.ComponentPropsWithRef<
C extends keyof JSX.IntrinsicElements | React.ComponentType<any> ? C : never
> &
O & { as?: C };
interface CustomComponent<
C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
O extends object
> {
<AsC extends keyof JSX.IntrinsicElements | React.ComponentType<any> = C>(
props: CustomComponentProps<AsC, O>
): React.ReactElement<CustomComponentProps<AsC, O>>;
}
const Button: CustomComponent<"button", { variant: "primary" }> = (props) => (
<button {...props} />
);
<Button variant="primary">Test</Button>;
<Button variant="primary" to="/test">
Test
</Button>;
<Button variant="primary" as={Link} to="/test">
Test
</Button>;
<Button variant="primary" as={Link}>
Test
</Button>;
我從 styled-components 中刪除了很多比這更復雜的東西。 例如,他們有一些解決方法來處理我刪除的 class 組件。 所以這個片段可能需要針對高級用例進行定制。
我發現你可以用 JSX.IntrinsicElements 做同樣的事情。 我有面板元素:
export type PanelAsKeys = 'div' | 'label'
export type PanelAsKey = Extract<keyof JSX.IntrinsicElements, PanelAsKeys>
export type PanelAs<T extends PanelAsKey> = JSX.IntrinsicElements[T]
export type PanelAsProps<T extends PanelAsKey> = Omit<PanelAs<T>, 'className' | 'ref'>
我省略了像 ref 和 className 這樣的原生類型,因為我對這些字段有自己的類型
這就是道具的樣子
export type PanelProps<T extends PanelAsKey = 'div'> = PanelAsProps<T> & {}
const Panel = <T extends PanelAsKey = 'div'>(props: PanelProps<T>) => {}
然后你可以使用 React.createElement
return React.createElement(
as || 'div',
{
tabIndex: tabIndex || -1,
onClick: handleClick,
onFocus: handleFocus,
onBlur: handleBlur,
onMouseEnter: handleMouseEnter,
onMouseLeave: handleMouseLeave,
'data-testid': 'panel',
...rest
},
renderChildren)
在這種情況下,我看不到 ts 錯誤,並且完成了 label 道具(如 htmlFor)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.