如何写好一个react组件

作者:frank 发表日期:2016-04-17 14:36:59 更新日期:2016-06-15 20:18:08 分类:猿文色

摘要

如何写好一个react组件

正文

  1. 设置默认属性 defaultProps
  2. 属性检测 propTypes
  3. 构造函数:获取默认值,绑定函数
  4. Controlled Componennt:更新Value
  5. 更新state,回调onChange
import React from 'react';
import RaisedButton from 'material-ui/lib/raised-button';

//定义内部样式
const minBtnStyle = {
  height: '30px',
  margin: '5px 0',
  marginLeft: '10px',
  minWidth: 'auto'
};

const minBtnLabelStyle = {
  lineHeight: '30px'
};

class BSelect extends React.Component {

  //1、设置默认属性
  static defaultProps = {
    /* 选择列表 */
    checkList: [],

    /* 根元素类名 */
    className: undefined,

    /* 默认值,multi为true时,是数组 */
    defaultValue: undefined,

    /* 组件说明文字 */
    label: undefined,

    /* 是否启用多选 */
    multi: false,

    /* 选中的值,是否是Controlled Components  */
    value: undefined,

    /* event  */
    onChange: undefined
  }

  //2、属性检测
  static propTypes = {
    checkList: React.PropTypes.array.isRequired,
    className: React.PropTypes.string,
    defaultValue: (props, propName) => {
      if(props.multi) {
        if(props[propName] && !(props[propName] instanceof Array)) {
          return new Error('defaultValue should be Array cause multi is true');
        }
      }
    },
    label: React.PropTypes.string,
    multi: React.PropTypes.bool,
    value: (props, propName) => {
      if(props.multi) {
        if(props[propName] && !(props[propName] instanceof Array)) {
          return new Error('value should be Array cause multi is true');
        }
      }
    },
    onChange: React.PropTypes.func
  }

  //3、构造函数:获取默认值,绑定函数
  constructor(props) {
    super(props);

    this._updateState = this._updateState.bind(this);
    this._setValue = this._setValue.bind(this);

    this.isMulti = this.props.multi;

    //init states
    this.state = {
      value: this.props.defaultValue === undefined ? this.isMulti ? [] : '' : this.props.defaultValue 
    };
  }

  componentWillMount() {}

  //4、Controlled Componennt:更新Value
  componentWillReceiveProps(nextProps) {
    this._setValue(nextProps, nextProps.value);
  }

  _setValue(props, value) {
    if(value !== undefined) {
      if(this.isMulti) {
        this.state.value = value === undefined ? [] : value;
      } else {
        this.state.value = value === undefined ? '' : value;
      }
    }
  }

  //5、更新state,回调onChange
  _updateState() {
    if(typeof this.props.onChange === 'function') {
      this.props.onChange(this.state.value);
    }

    this.setState({
      value: this.state.value
    });
  }

  render() {
    let labelText = this.props.label;
    let labelElement = labelText ? <label>{labelText + ':'}</label> : '';
    return (
      <div className={'row pdl1 pdr1 middle-xs ' + (this.props.className || '')}>
        {labelElement}
        <div className='col-xs nopadding'>
          {
            this.props.checkList.map((v, i) => {
              return (
                <RaisedButton className={v.btnClassName}
                              disabled={v.disabled}
                              label={v.label}
                              labelStyle={minBtnLabelStyle}
                              key={i}
                              onClick={() => {
                                if(this.isMulti) {
                                  let index = this.state.value.indexOf(v.value);
                                  if(index !== -1) {
                                    this.state.value.splice(index, 1);
                                  } else {
                                    this.state.value.push(v.value);
                                  }
                                } else {
                                  this.state.value = v.value; 
                                }
                                this._updateState();
                              }}
                              secondary={(() => {
                                if(this.state.value instanceof Array) {
                                  return this.state.value.includes(v.value);
                                } else {
                                  return this.state.value === v.value;
                                }
                              })()}
                              style={minBtnStyle}/>
              )
            })
          }
        </div>
      </div>
    )
  }
}

export default BSelect;

BSelect