ANt-designのソースコードを読みます.Button
7130 ワード
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
)
)