-->

How to wrap up Ant Design with Styled Components a

2020-06-14 07:16发布

问题:

I want to wrap up my ant-design components with styled-components, I know that this is possible (https://gist.github.com/samuelcastro/0ff7db4fd54ce2b80cd1c34a85b40c08) however I'm having troubles to do the same with TypeScript.

This is what I have so far:

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 you can see I'm defining my ant-design button with as any in order to make it work, otherwise I get some incompatible types like:

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
 }

Thank you.

Solution:

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
`;

回答1:

I have found this ancient question and try to solve in an easy way:

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;
`;

without render props :D

if you just need wrap a component as function component, that's all right. But you will lose the properties of class component such as Card.Meta.

There is a workaround:

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;

Everything (maybe... XD) works as original Antd Component ;)



回答2:

The root of the problem seems to be that styled-components expects the inner component (AntButton) to accept all the props in the specified interface (IButtonProps), but AntButton does not accept customProp. To fix this, follow the last example in this section of the documentation and use a stateless function component to remove customProp before calling AntButton.

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


回答3:

The above solutions didn't work for me, this solved it though.

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


回答4:

index.tsx (Button Component)

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