如何将道具传递给样式化组件而不将它们转换为瞬态道具?

问题描述 投票:0回答:2

我创建了一个

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>
  );
};
javascript css reactjs typescript styled-components
2个回答
1
投票

这是一个使用

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>
    </>
  );
};

这是一个沙箱所以你可以看到它的工作。


0
投票

您应该拥有一个默认的 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>

你也可以使用字符串而不是枚举作为变体的值,但在开发中使用枚举来节省构建空间和内存是常见的做法

© www.soinside.com 2019 - 2024. All rights reserved.