ant - design 소스 코드 읽 기Button

버튼 구성 요소
전체
  • 버튼 구성 요소
  • 주로 state 상 태 를 바 꾸 어 렌 더 링 할 때 서로 다른 className 을 사용 하여 외관 을 바 꿉 니 다
  • 중국어 문자 가 2 개 밖 에 없 는 지 확인 합 니 다. 예 를 들 어 '제출' 은 state 의 속성 을 변경 하고 다시 렌 더 링 할 때 '제출'
  • 으로 변 합 니 다.
  • componentWillReceiveProps 에서 loading 상태 가 필요 한 지 확인 합 니 다
  • 렌 더 링 된 dom 에 click 이 연결 되 어 있 습 니 다. setTimeout 방법 으로 click 애니메이션 을 지연 시 킵 니 다
  • 주의 하 다.
  • classNames 와 omit 사용 배우 기
  • React. clone Element 의 사용
  • React. children 을 사용 하 는 것 과 this. props. children 을 사용 하 는 것 을 비교 하면 전자 가 더 정확 하 다
  • 매번 render 는 className 을 변경 하여 외관 과 애니메이션 을 변경 합 니 다
  • 원본 주석
    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 = (findDOMNode(this) as HTMLElement);
    / / button 의 값 을 되 돌려 줍 니 다 (예: 클릭)
    const buttonText = node.textContent || node.innerText;
    / / 1 개의 키 요소 만 있 고 이 하위 요 소 는 2 개의 중국어 문자 입 니 다.
    if (this.isNeedInserted() && isTwoCNChar(buttonText)) {
    / / state 의 hasTowCNChar 를 true 로 변경
    if (!this.state.hasTwoCNChar) {
    this.setState({
    hasTwoCNChar: true,
    });
    }
    } else if (this.state.hasTwoCNChar) {
    / / 조건 에 맞지 않 으 면 hasTowCNChar 가 true (이 구성 요소 가 2 개의 중문 자 였 음 을 설명 하고 나중에 update 는 아 닙 니 다) 로 false 로 변 합 니 다.
    this.setState({
    hasTwoCNChar: false,
    });
    }
    }
    /**
    * 클릭 할 때마다 state. clicked 를 변경 하여 class 를 변경 하여 시각 적 효 과 를 변경 합 니 다.
    */
    handleClick = (e: React.MouseEvent) => {
    // Add click effect
    this.setState({ clicked: true });
    clearTimeout(this.timeout);
    this.timeout = window.setTimeout(() => this.setState({ clicked: false }), 500);
    const onClick = this.props.onClick;
    / / onClick 존재, 실행
    if (onClick) {
    (onClick as (e: React.MouseEvent) => void)(e);
    }
    }
    / / 키 요소 1 개 만 있 고 icon 이 없습니다.
    isNeedInserted() {
    const { icon, children } = this.props;
    return React.Children.count(children) === 1 && !icon;
    }
    render() {
    const {
    type, shape, size, className, htmlType, children, icon, prefixCls, ghost, ...others,
    } = this.props;
    const { loading, clicked, hasTwoCNChar } = this.state;
    // large => lg
    // small => sm
    let sizeCls = '';
    switch (size) {
    case 'large':
    sizeCls = 'lg';
    break;
    case 'small':
    sizeCls = 'sm';
    default:
    break;
    }
    const ComponentProp = (others as AnchorButtonProps).href ? 'a' : 'button';
    / / classNames 는 아래 class 를 "xx 1 xx 2 xx 3..." 형식 으로 변경 할 수 있 습 니 다.
    / / 그 중 값 은 true, key 가 성립 되 고 값 은 false 이 며 key 는 합병 되 지 않 습 니 다.
    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,
    [`${prefixCls}-two-chinese-chars`]: hasTwoCNChar,
    });
    const iconType = loading ? 'loading' : icon;
    const iconNode = iconType ?  : null;
    / / this. props. children 이 존재 할 때 모든 children 형식 에 대해 판단 (null, react 구성 요소, 문자열) 을 실행 하고 판단 에 따라 추가 작업 을 수행 합 니 다.
    const kids = (children || children === 0)
    ? React.Children.map(children, child => insertSpace(child, this.isNeedInserted())) : null;
    /**
    * omit: others 에서 loading 속성 삭제
    * type: href 속성 (a 탭) 이 undefined 로 설정 되 어 있 으 면 button 이면 입력 값 이나 'button' 으로 설정 합 니 다.
    *
    */
    return (
    {iconNode}{kids}
    );
    }
    }

    좋은 웹페이지 즐겨찾기