我创建了一个
Text
组件,它接收几个道具。这些道具被传递到应用样式的样式组件。我不想将道具传递给 DOM,我只想在样式组件中访问它们。
将 props 转换为 transient props 是可行的,但它会创建重复代码,我认为这会使整个组件不必要地变大。
我想知道,如何将一些 props 传递给我的样式组件而不将它们传递给 DOM 并且不必重新定义每个 prop。我尝试使用
shouldForwardProps
但我无法让它与 Typescript 一起工作。
type Props = {
children: React.ReactNode;
size: number;
height: number;
color: string;
};
const StyledText = styled.p<{ $color: string; $size: number; $height: number }>`
color: ${({ $color }) => $color};
font-size: ${({ $size }) => `${$size}px;`};
line-height: ${({ $height }) => `${$height}px;`};
...
`;
const Text: React.FC<Props & React.HTMLAttributes<HTMLParagraphElement>> = ({ children, size, height, color, ...rest }) => {
return (
<StyledText $size={size} $height={height} $color={color} {...rest}>
{children}
</StyledText>
);
};
这是一个使用
shouldForwardProps
的解决方案,我相信这就是您所要求的。我曾尝试将 customAttributeNames
与 Props
联系起来,但我已经放弃了。
const customAttributeNames = ["size", "height", "color"] as const;
type TCustomAttribute = typeof customAttributeNames[number];
type Props = React.HTMLAttributes<HTMLParagraphElement> & {
children: React.ReactNode;
size: number;
height: number;
color: string;
};
const StyledTextWithoutDomAttributes = styled.p.withConfig({
shouldForwardProp: (prop) =>
!(customAttributeNames as readonly string[]).includes(prop)
})<Pick<Props, TCustomAttribute>>`
color: ${({ color }) => color};
font-size: ${({ size }) => `${size}px;`};
line-height: ${({ height }) => `${height}px;`};
`;
const StyledTextWithDomAttributes = styled.p<Pick<Props, TCustomAttribute>>`
color: ${({ color }) => color};
font-size: ${({ size }) => `${size}px;`};
line-height: ${({ height }) => `${height}px;`};
`;
const Text: React.FC<Props> = ({ children, size, height, color, ...rest }) => {
return (
<>
<StyledTextWithDomAttributes
size={size}
height={height}
color={color}
{...rest}
>
{children}
</StyledTextWithDomAttributes>
<StyledTextWithoutDomAttributes
size={size}
height={height}
color={color}
{...rest}
>
{children}
</StyledTextWithoutDomAttributes>
</>
);
};
这是一个沙箱所以你可以看到它的工作。
您应该拥有一个默认的 StyledText 组件,然后使用
styled
函数覆盖该组件
const StyledTextPrimary = styled.p`
color: #000;
font-size: 1rem;
line-height: 130%;
`;
然后你像这样覆盖它
const StyledTextLarge = styled(StyledText)`
color: #333333;
font-size: 1.2rem;
line-height: 140%;
`;
通常在设计系统中,您定义文本/排版的不同变体。它是有限的(即:标题、副标题、正文)。因此,最好有这样的不同组件,而不是影响最终用户传递可能不符合您想象的设计的变量
如果您需要维护大量文本组件,则可以创建 1 个组件,该组件采用属性 « variant » 并根据 switch case 或哈希表返回正确的组件
这里有一个完整的打字稿例子
export enum ETextVariants {
primary,
large,
}
const variants = {
[ETextVariants.primary]: StyledTextPrimary,
[ETextVariants.large]: StyledTextLarge,
};
export interface ITextProps extends ComponentPropsWithoutRef<'p'> {
variant?: ETextVariants;
}
export function StyledText({
variant = ETextVariants.primary,
children,
...props
}: ITextProps) {
const TextComponent = variants[variant];
return (
<TextComponent {...props}>
{children}
</TextComponent>
);
}
然后你像这样使用它
<StyledText>my text</StyledText>
// or
<StyledText variant={ETextVariants.large}>my text</StyledText>
你也可以使用字符串而不是枚举作为变体的值,但在开发中使用枚举来节省构建空间和内存是常见的做法