ANt-designのソースコードを読みます.Button


Buttonコンポーネント
全体
  • Buttonコンポーネント
  • は主にstate状態を変えることによってレンダリング時に異なるクラスNameを適用して外観を変更する
  • である.
  • は、中国語の文字が2つしかないかどうかをチェックします.例えば「提出」は、チェックするとstateの属性が変更され、再度レンダリングすると「提出」
  • になります.
  • componentWillreceive Propsでは、loading状態が必要かどうかをチェックします.
  • レンダリングされたdomには、clickが結合されており、setTimeout方式により、clickアニメーション
  • の実行が遅延される.
    注意
  • 学習用クラスNamesとomit
  • React.cloneElementの使用
  • React.Childrenを使用することと、this.props.childrenを使用することとの比較は、前者がより正確である
  • .
  • 毎回renderはクラスNameを変えることによって外観とアニメ
  • を変える.
    ソースのコメント
    import * as React from 'react';
    import { findDOMNode } from 'react-dom';
    import PropTypes from 'prop-types';
    import classNames from 'classnames';
    import omit from 'omit.js';
    import Icon from '../icon';
    import Group from './button-group';
    
    //2     
    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.
      // child react             2     
      //      ,child   obj,  type "span"
      if (typeof child !== 'string' && typeof child !== 'number' &&
        isString(child.type) && isTwoCNChar(child.props.children)) {
        //     (element,[props],[...children])
        //      
        // {child.props.children.split('').join(SPACE)}
        return React.cloneElement(child, {},
          child.props.children.split('').join(SPACE));
      }
      // child    
      if (typeof child === 'string') {
        // child  2     
        if (isTwoCNChar(child)) {
          child = child.split('').join(SPACE);
        }
        return {child};
      }
      return child;
    }
    
    export type ButtonType = 'default' | 'primary' | 'ghost' | 'dashed' | 'danger';
    export type ButtonShape = 'circle' | 'circle-outline';
    export type ButtonSize = 'small' | 'default' | 'large';
    
    export interface BaseButtonProps {
      type?: ButtonType;
      htmlType?: string;
      icon?: string;
      shape?: ButtonShape;
      size?: ButtonSize;
      loading?: boolean | { delay?: number };
      prefixCls?: string;
      className?: string;
      ghost?: boolean;
    }
    
    export type AnchorButtonProps = BaseButtonProps & React.AnchorHTMLAttributes;
    
    export type NativeButtonProps = BaseButtonProps & React.ButtonHTMLAttributes;
    
    export type ButtonProps = AnchorButtonProps | NativeButtonProps;
    
    export default class Button extends React.Component {
      static Group: typeof Group;
      static __ANT_BUTTON = true;
    
      static defaultProps = {
        prefixCls: 'ant-btn',
        loading: 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,
          clicked: false,
          hasTwoCNChar: false,
        };
      }
    
      //     
      componentDidMount() {
        //         2   ,    state
        this.fixTwoCNChar();
      }
    
      //  prop  
      componentWillReceiveProps(nextProps: ButtonProps) {
        const currentLoading = this.props.loading;
        const loading = nextProps.loading;
    
        //    loading
        if (currentLoading) {
          //     loading    
          clearTimeout(this.delayTimeout);
        }
        //loading     ,    delay  (   loadidng  )
        if (typeof loading !== 'boolean' && loading && loading.delay) {
          this.delayTimeout = window.setTimeout(() => this.setState({ loading }), loading.delay);
        } else {
          //    loading  
          this.setState({ loading });
        }
      }
    
      //    render ,    2    
      componentDidUpdate() {
        this.fixTwoCNChar();
      }
    
      componentWillUnmount() {
        if (this.timeout) {
          clearTimeout(this.timeout);
        }
        if (this.delayTimeout) {
          clearTimeout(this.delayTimeout);
        }
      }
    
      //         2   ,        state.hasTwoCNChar
      fixTwoCNChar() {
        // Fix for HOC usage like 
        //       DOM(     または)
    const node=(findDOM Node)as HTMlement;
    //button の を します( :クリック)
    const buttonText=node.text Content𞓜node.inneratext;
    //1つのサブ  だけで、このサブ  は2つの     です。
    if(this.isNeed Inserted()&isTwo CNChar(buttonText){
    //stateのhasTowCNCharをtrueに  する
    if(!this.state.has TwoCNChar){
    this.set State({
    ハスTwoCNChar:true、
    }
    )
    }else if(this.state.has TwoCNChar){
    //  に わない  は、has TowCNCharがtrueとなります(このコンポーネントを  する に2つの  で、その udateは います)、falseになります。
    this.set State({
    ハスTwoCNChar:false、
    }
    )
    )
    /**
    *  clickはstate.clickedを えてクラスを  し、    を える
    */
    handleClick=(e:React.MouseEvent)=>
    //Add click effect
    this.set State({clicked:true}
    clearTimeout(this.timeout)
    this.timeout=window.set Timeout()=>this.set State({clicked:false}500);
    const onClick=this.props.onClick;
    //onClickが  し、  する
    if(onClick){
    (onClick as(e:React.MouseEvent)=>void(e)
    )
    )
    //1つのサブ  だけで、かつ、iconがありません。
    isNeed Inserted(){
    const{icon,children}=this.props;
    return React.Children.com==1&!icon
    )
    レンダー(){
    const{
    type、shpe、size、クラスName、htmlType、チルドレン、icon、prefixClas、ghost、…others、
    }=this.props
    const{loading、clicked、has TwoCNChar}=this.state;
    //large=>lg
    //small=>sm
    let sizecs=''
    switch(size){
    case'large':
    sizecs='lg'
    break;
    case'small':
    sizecs='sm'
    default:
    break;
    )
    const ComponentProp=(others as AnchorButonProps).href?'a':'button';
    //class Namesは  のクラスを「×1×2×3…」のフォーマットに えることができます。
    //その の はtrueで、keyは  して、 はfalseで、keyは  しません。
    const clases=class Names(prefixClas、class Name){
    [${prefixClas}-${type}:type、
    [$prefixClas]-$
    [$prefixClas]-$
    [${prefixClas}-icon-only`]:!children&&&icon、
    [$prefixClas]-loading`:loading、
    [$prefixClas]-clicked`:clicked、
    [$prefixClas]-background-ghost`。
    [$prefixClas]-two-chinese-chars`:has TwoCNChar,
    }
    const iconType=loading?'loading':icon;
    const iconNode=iconType?:null;
    //this.props.Childrenが  する  は、 childrenタイプについて  (null、reactコンポーネント、   )を い、  に づいて    を  します。
    const kids=(チルドレンチルドレン==0)
    ?React.Children.map(children,child=>insertSpace(child,this.isNeed Inserted():null;
    /**
    *omit:othersからloadingという  を  する
    *type:href  (aラベル)があればundefinedとし、buttonであれば   または'button'として  します。
    *
    */
    リターン(
    {iconNode}{kids}
    )0
    )
    )