Button

Button consists of two components, Button and ButtonGroup.

ButtonProps

The first thing you need to look at a component is its props, so let’s look at the ButtonProps of a Button component

  export type ButtonType = 'primary' | 'ghost' | 'dashed' | 'danger';
  export type ButtonShape = 'circle' | 'circle-outline';
  export type ButtonSize = 'small' | 'large';

  // typescript syntax, where parameters are represented. Optional parameter optional parameter mandatory parameter
  // The type of the parameter can be a custom type, such as' ButtonType ', 'ButtonShape', 'ButtonSize'.
  // Can also be a function or class, such as react. FormEventHandler
      
  / / details described here at https://www.tslang.cn/docs/handbook/interfaces.html
  exportinterface ButtonProps { type? : ButtonType; htmlType? : string; icon? : string; shape? : ButtonShape; size? : ButtonSize; onClick? : React.FormEventHandler<any>; onMouseUp? : React.FormEventHandler<any>; onMouseDown? : React.FormEventHandler<any>; loading? : boolean | { delay? : number }; disabled? : boolean; style? : React.CSSProperties; prefixCls? : string; className? : string; ghost? : boolean; }Copy the code

Render()

So once we see what the parameters are we’re going to skip the rest of the stuff inside the component, and we’re going to go straight to the render function, because this is the entry point to the execution and I’ll mention this code by the way

// We are passing in two parameters, the first is Props, the second is state,
// Then use typescript type checking to check that the Props type requires the variable name in the optional parameter in ButtonProps defined above
// state can be passed either way
export default class Button extends React.Component<ButtonProps.any>Copy the code
// Next render()
  render() {
    // Decompose the parameter from props
    const {
      type, shape, size = ' ', className, htmlType, children, icon, prefixCls, ghost, ... others, } =this.props;
    // Deconstruct the loading and clicked states from state
    const { loading, clicked } = this.state;

    // large => lg
    // small => sm
    let sizeCls = ' ';
    switch (size) {
      case 'large':
        sizeCls = 'lg';
        break;
      case 'small':
        sizeCls = 'sm';
      default:
        break;
    }
    // Build style
    const classes = classNames(prefixCls, className, {
      [`${prefixCls}-${type}`]: type,
      [`${prefixCls}-${shape}`]: shape,
      [`${prefixCls}-${sizeCls}`]: sizeCls,
      [`${prefixCls}-icon-only`] :! children && icon, [`${prefixCls}-loading`]: loading,
      [`${prefixCls}-clicked`]: clicked,
      [`${prefixCls}-background-ghost`]: ghost,
    });

    // Whether to load
    const iconType = loading ? 'loading' : icon;
    // Do you need to add Icon? However, the official advice is that if Icon is needed, you'd better write it in it yourself

    const iconNode = iconType ? <Icon type={iconType} />: null; const needInserted = React.Children.count(children) === 1 && (! iconType || iconType === 'loading'); // The function insertSpace() is used to render the contents wrapped in the Button component. This function is used to add a space between Chinese characters when they are written in the middle. Some students will ask why they don't use the letter-space // property in CSS. Const kids = react.children. Map (Children, child => insertSpace(child, needInserted)); return (<button/ / and stillIconComponents like that will not be usedpropsTo get rid of {. omit(othersAnd ['loading', 'clicked'])}
        type={htmlType| | 'button'}
        className={classes}
        onClick={this.handleClick}
      >
        {iconNode}{kids}
      </button>
    );
  }Copy the code

InsertSpace()

Now that we have this function, let’s take a closer look at what it does

  const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/;
  // Bind is a bind
  const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar);
  function isString(str: any) {
    return typeof str === 'string';
  }

  // Insert one space between two chinese characters automatically.
  function insertSpace(child: React.ReactChild, needInserted: boolean) {
    // Check the child if is undefined or null.
    if (child == null) {
      return;
    }
    const SPACE = needInserted ? ' ' : ' ';
    // strictNullChecks oops.
    Type is a string and the child's children are Chinese characters
    // Add a space to it, the above is the literal translation of the code, then the free translation of the code is such a case
    // This is the case (so here he has an English comment, which is not a strict check, haha, awkward implementation method)
    // <Button>
    // 
      
// </Button> // Child-type and child-props. Children are properties that react adds to the virtual DOM when rendering if (typeofchild ! = ='string' && typeofchild ! = ='number' && isString(child.type) && isTwoCNChar(child.props.children)) { return React.cloneElement(child, {}, child.props.children.split(' ').join(SPACE)); } // This is a clear case of the Chinese characters in the Button component if (typeof child === 'string') { if (isTwoCNChar(child)) { child = child.split(' ').join(SPACE); } return <span>{child}</span>; } return child; }Copy the code

Button

Complete source code

The rest is simple stuff, and there’s nothing left to talk about, right

  import React from 'react';
  import PropTypes from 'prop-types';
  import classNames from 'classnames';
  import omit from 'omit.js';
  import Icon from '.. /icon';
  import Group from './button-group';

  const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/;
  const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar);
  function isString(str: any) {
    return typeof str === 'string';
  }

  // Insert one space between two chinese characters automatically.
  function insertSpace(child: React.ReactChild, needInserted: boolean) {
    // Check the child if is undefined or null.
    if (child == null) {
      return;
    }
    const SPACE = needInserted ? ' ' : ' ';
    // strictNullChecks oops.
    if (typeofchild ! = ='string' && typeofchild ! = ='number' &&
      isString(child.type) && isTwoCNChar(child.props.children)) {
      return React.cloneElement(child, {},
        child.props.children.split(' ').join(SPACE));
    }
    if (typeof child === 'string') {
      if (isTwoCNChar(child)) {
        child = child.split(' ').join(SPACE);
      }
      return <span>{child}</span>;
    }
    return child;
  }

  export type ButtonType = 'primary' | 'ghost' | 'dashed' | 'danger';
  export type ButtonShape = 'circle' | 'circle-outline';
  export type ButtonSize = 'small' | 'large';

  exportinterface ButtonProps { type? : ButtonType; htmlType? : string; icon? : string; shape? : ButtonShape; size? : ButtonSize; onClick? : React.FormEventHandler<any>; onMouseUp? : React.FormEventHandler<any>; onMouseDown? : React.FormEventHandler<any>; loading? : boolean | { delay? : number }; disabled? : boolean; style? : React.CSSProperties; prefixCls? : string; className? : string; ghost? : boolean; }export default class Button extends React.Component<ButtonProps.any> {
    Button.Group refers to the ButtonGroup component, which will be explained in the next section
    static Group: typeof Group;
    static __ANT_BUTTON = true;

    static defaultProps = {
      prefixCls: 'ant-btn'.loading: false.clicked: false.ghost: false};static propTypes = {
      type: PropTypes.string,
      shape: PropTypes.oneOf(['circle'.'circle-outline']),
      size: PropTypes.oneOf(['large'.'default'.'small']),
      htmlType: PropTypes.oneOf(['submit'.'button'.'reset']),
      onClick: PropTypes.func,
      loading: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
      className: PropTypes.string,
      icon: PropTypes.string,
    };

    timeout: number;
    delayTimeout: number;

    constructor(props: ButtonProps) {
      super(props);
      this.state = {
        loading: props.loading,
      };
    }

    componentWillReceiveProps(nextProps: ButtonProps) {
      const currentLoading = this.props.loading;
      const loading = nextProps.loading;

      if (currentLoading) {
        clearTimeout(this.delayTimeout);
      }

      if (typeofloading ! = ='boolean' && loading && loading.delay) {
        this.delayTimeout = setTimeout((a)= > this.setState({ loading }), loading.delay);
      } else {
        this.setState({ loading }); }}// The timer must be destroyed when the component is destroyed
    componentWillUnmount() {
      if (this.timeout) {
        clearTimeout(this.timeout);
      }
      if (this.delayTimeout) {
        clearTimeout(this.delayTimeout);
      }
    }

    handleClick = (e: React.MouseEvent<HTMLButtonElement>) = > {
      // Add click effect
      this.setState({ clicked: true });
      clearTimeout(this.timeout);
      this.timeout = setTimeout((a)= > this.setState({ clicked: false }), 500);

      const onClick = this.props.onClick;
      if (onClick) {
        onClick(e);
      }
    }

    render() {
      const {
        type, shape, size = ' ', className, htmlType, children, icon, prefixCls, ghost, ... others, } =this.props;

      const { loading, clicked } = this.state;

      // large => lg
      // small => sm
      let sizeCls = ' ';
      switch (size) {
        case 'large':
          sizeCls = 'lg';
          break;
        case 'small':
          sizeCls = 'sm';
        default:
          break;
      }

      const classes = classNames(prefixCls, className, {
        [`${prefixCls}-${type}`]: type,
        [`${prefixCls}-${shape}`]: shape,
        [`${prefixCls}-${sizeCls}`]: sizeCls,
        [`${prefixCls}-icon-only`] :! children && icon, [`${prefixCls}-loading`]: loading,
        [`${prefixCls}-clicked`]: clicked,
        [`${prefixCls}-background-ghost`]: ghost,
      });

      const iconType = loading ? 'loading' : icon;
      const iconNode = iconType ? <Icon type={iconType} />: null; const needInserted = React.Children.count(children) === 1 && (! iconType || iconType === 'loading'); const kids = React.Children.map(children, child => insertSpace(child, needInserted)); return (<button
          {. omit(othersAnd ['loading', 'clicked'])}
          type={htmlType| | 'button'}
          className={classes}
          onClick={this.handleClick}
        >
          {iconNode}{kids}
        </button>); }}Copy the code