如何写好一个react组件

正文

  1. 设置默认属性 defaultProps
  2. 属性检测 propTypes
  3. 构造函数:获取默认值,绑定函数
  4. Controlled Componennt:更新Value
  5. 更新state,回调onChange
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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