如何用样式化组件和 TypeScript 包装 Ant Design?

Posted

技术标签:

【中文标题】如何用样式化组件和 TypeScript 包装 Ant Design?【英文标题】:How to wrap up Ant Design with Styled Components and TypeScript? 【发布时间】:2019-03-13 06:33:31 【问题描述】:

我想用 styled-components 包装我的 ant-design 组件,我知道这是可能的 (https://gist.github.com/samuelcastro/0ff7db4fd54ce2b80cd1c34a85b40c08) 但是我在使用 TypeScript 时遇到了麻烦。

这是我目前所拥有的:

import  Button as AntButton  from 'antd';
import  ButtonProps  from 'antd/lib/button/button';
import styledComponents from 'styled-components';

interface IButtonProps extends ButtonProps 
   customProp: string;


export const Button = styledComponents<IButtonProps>(AntButton)`
  // any custom style here
`;

如您所见,我使用 as any 定义了我的 ant-design 按钮以使其正常工作,否则我会得到一些不兼容的类型,例如:

Argument of type 'typeof Button' is not assignable to parameter of
type 'ComponentType<IButtonProps>'.

Type 'typeof Button' is not assignable to type
'StatelessComponent<IButtonProps>'.

Types of property 'propTypes' are incompatible.

 Property 'customProp' is missing in type ' 
    type: Requireable<string>; 
    shape: Requireable<string>; 
    size: Requireable<string>; 
    htmlType: Requireable<string>; 
    onClick: ...
    etc
 

谢谢。

解决方案:

import  Button as AntButton  from 'antd';
import  NativeButtonProps  from 'antd/lib/button/button';
import * as React from 'react';
import styledComponents from 'styled-components';

export const Button = styledComponents<NativeButtonProps>(props => <AntButton ...props />)`
    // custom-props
`;

【问题讨论】:

如果您能更具体一点会有所帮助 - 您面临的具体问题是什么?您可以提供的信息越多(最好也显示您的代码),更多的人将能够提供帮助:) @JoeClay 谢谢添加更多信息。 请将您的导入添加到问题中。我猜import styledComponents from "styled-components"import Button as AntButton from "antd" 但我猜不出IButtonProps 来自哪里。 @MattMcCutchen 添加了导入。谢谢! 刚试过:export const Button = styledComponents&lt;IButtonProps, any&gt;(AntButton) 好像还可以,就是不知道styledComponents&lt;IButtonProps, any&gt; 的第二个类型参数是什么意思。 【参考方案1】:

问题的根源似乎是styled-components期望内部组件(AntButton)接受所有指定接口(@98​​7654324@)中的props,但是AntButton不接受customProp。要解决此问题,请按照文档的this section 中的最后一个示例,并在调用AntButton 之前使用无状态函数组件删除customProp

export const Button = styledComponents<IButtonProps>(
  ( customProp, ...rest ) => <AntButton ...rest />)`
  // any custom style here
`;

【讨论】:

谢谢马特,这工作得很好,但是现在当我尝试使用我的新按钮组件时:&lt;Button onClick=prop.onChange/&gt; Typescript 抱怨:Type ' onClick: any; ' is not assignable to type 'Readonly&lt;ThemedOuterStyledProps&lt;WithOptionalTheme&lt;ButtonProps, ThemeInterface&gt;, ThemeInterface&gt;&gt;'. Property 'style' is missing in type ' onClick: any; '. 好的,我解决了这个问题,从 antd 导入 NativeButtonProps 而不是 ButtonProp【参考方案2】:

上述解决方案对我不起作用,但这解决了它。

const Button = styled((props: NativeButtonProps) => <AntButton ...props />)``;

【讨论】:

【参考方案3】:

index.tsx(按钮组件)

import  Button as AntButton  from 'antd'
import  NativeButtonProps  from 'antd/lib/button/button'
import 'antd/lib/button/style/css'
import * as React from 'react'
import styledComponents from 'styled-components'
import * as colours from '../colours'

const getColour = (props: any) =>
  props.status === 'green'
    ? colours.STATUS_GREEN
    : props.status === 'red'
      ? colours.STATUS_RED
      : props.type === 'primary'
        ? colours.PRIMARY
        : colours.WHITE

export interface ButtonProps extends NativeButtonProps 
  status?: string


export default styledComponents((props: ButtonProps) => <AntButton ...props />)`
  &:focus,
  &:hover
  & 
    background-color: $getColour;
    border-color: $getColour;
  
`
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.5.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.2/umd/react-dom.production.min.js"></script>

import React from 'react'
import Button,  ButtonProps  from './index'

interface ButtonAsyncSingleSuccessProps extends ButtonProps 
  clickFunc: any, // (...args: any[]) => Promise<any>
  labelLoading: string,
  labelReady: string,
  labelSuccess: string,


interface ButtonAsyncSingleSuccessState 
  label: string,
  loading: boolean,
  status: string


export default class ButtonAsyncSingleSuccess extends React.Component<
  ButtonAsyncSingleSuccessProps,
  ButtonAsyncSingleSuccessState
> 
  constructor (props: any) 
    super(props)
    this.state = 
      label: props.labelReady,
      loading: false,
      status: ''
    
  
  public clickHandler (event: any) 
    const  labelLoading, labelReady, labelSuccess, clickFunc  = this.props
    this.setState(
      label: labelLoading,
      loading: true,
      status: ''
    )
    clickFunc(event)
      .then(() => 
        this.setState(
          label: labelSuccess,
          loading: false,
          status: 'green'
        )
      )
      .catch(() => 
        this.setState(
          label: labelReady,
          loading: false,
          status: 'red'
        )
      )
  
  public render () 
    const 
      labelLoading,
      labelReady,
      labelSuccess,
      clickFunc,
      ...props
     = this.props
    const  label, loading, status  = this.state
    if (status === 'red') 
      setTimeout(() => this.setState( status: '' ), 1000) // flash red
    
    return (
      <Button
        ...props
        loading=loading
        status=status
        onClick=(e) => this.clickHandler(e)
      >
        label
      </Button>
    )
  

【讨论】:

如果我想让 AntdButton 可重用并在每种情况下传递不同的样式怎么办? 我认为您可以通过传递不同的样式名称道具轻松做到这一点。【参考方案4】:

我发现了这个古老的问题并尝试以一种简单的方式解决:

import React from 'react';
import styled from 'styled-components';
import  Card  from 'antd';
import  CardProps  from 'antd/lib/card';

export const NewCard: React.FunctionComponent<CardProps> = styled(Card)`
  margin-bottom: 24px;
`;

没有渲染道具:D

如果你只需要将一个组件包装为函数组件,那没关系。但是你会丢失类组件的属性,例如Card.Meta

有一个解决方法:

import React from 'react';
import styled from 'styled-components';
import  Card  from 'antd';
import  CardProps  from 'antd/lib/card';

export const NewCard: typeof Card = styled(Card)<CardProps>`
  margin-bottom: 24px;
` as any;

一切(也许...... XD)都作为原始 Antd 组件工作;)

【讨论】:

【参考方案5】:

我的代码在这里。这是工作。


import React from 'react';
import  Button as AntButton  from 'antd';
import  ButtonProps  from 'antd/lib/button/button';
import styled from 'styled-components';

const Container = styled.div`
  font-size: 20px;
`;


const Button: React.FunctionComponent<ButtonProps> = styled(AntButton)`
  margin-top: 24px;
  margin-left: 30px;
`;

export default function Home() 
  return (
    <Container>
      Hello World
      <Button type="primary">test</Button>
    </Container>
  );




【讨论】:

以上是关于如何用样式化组件和 TypeScript 包装 Ant Design?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 react-testing-library 测试包装在 withStyles 中的样式化 Material-UI 组件?

React 和 Typescript,样式化的组件和子组件

带有 Typescript 和样式化组件的 RN FlatList

带有 Typescript 和 ThemeProvider 的样式化组件。啥是正确的类型?

如何将样式化组件作为属性添加到 TypeScript 中的另一个样式化组件?

如何使用 Material UI 和 TypeScript 将自定义道具传递给样式化组件?