React入门笔记:一个小表格组件的构建过程

vue,angular,react,这三个框架,本来打算学vue。图书馆资料就一本,就转向了React(图书馆突然买了好多。。。),其实还是因为它能够使用native开发,现在开发安卓我用的是kotlin,想着前端学习,也要加把劲了,迈入更好的路。

一直在印象笔记写:

1:组件入门

TIPS:引入库文件(现在还没到jsx,用了两个,一个核心,一个dom库)

1
2
3
<script src="react.js"></script>

<script src="react-dom.js"></script>

1.1使用预定义组件(这些是预定的组件构造器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ReactDOM.render();//渲染函数

ReactDOM.render(“what”,where);//绑定用id

React.DOM.*({JSON属性},子节点}//属性省略null, 子节点可以嵌套,组件构造器

<div id="example"></div>

ReactDOM.render(

React.DOM.h1(null,"你好,React"),

document.getElementById('example')

);

等同于:<div><h1>你好</h1></div>

1.2:嵌套使用:(说实话,没用JSX时候,复杂的时候,我都写蒙了,找不到括号匹配了。。。)

1
2
3
4
5
6
7
8
9
ReactDOM.render(

React.DOM.span({style:{color:"red"}},

React.DOM.em({style:{color:"blue"}}," 我是斜体蓝色span"),"我是正常红色span"),

document.getElementById('ex1')

);

等同于:

1
2
3
4
5
6
7
8
9
<div id="ex1">

<span style="color:"blue">我是斜体蓝色

<em style="color:"red">我是正常红色</em>

</span>

</div>

1.3.使用自定义组件(这个以后是主要使用的啦)

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
var myELe = React.createClass(

{

propTypes:{

name:React.PropTypes.string.isRequired,

title:React.PropTypes.string

},

getDefaultProps:function(){

return {

title:"我是默认属性值"

}

},

render:function(){

return React.DOM.span(null,"我是自定义组件span"+this.props.title)//调用属性值

}

});//自定义组件函数

React.createClass();//参数,json格式,render渲染函数,返回React组件



ReactDOM.render(

React.createElement(myEle),

document.getElementById('ex3')

);//引用



工厂模式:



var myEle2 = React.createFactory(myEle);

ReactDOM.render(

myele2(),

document.getElementById('ex3')

);
tips:默认属性值,只能返回非isRequired的属性值,设置接收属性值的对象:propTypes:{属性名:属性值,React.PropTypes.值类型.是否必须

创建组件,渲染传入需要接收的属性值,如果非isRequired没有传,则使用getDefaultProps函数返回的值!

在定义组件对象时,this.props.属性名,可以调用属性



1.4 State状态(对象里面值为函数,称方法)

下面是一个完整的自定义组件与渲染

var myIput = React.createClass({

//默认属性json格式

proptypes:{

text:React.PropTypes.string

},

//默认属性设置函数

getDefaultProps:function(){

return {

text:''

}

},

//初始化状态函数,动态设置属性值

getInitialState:function(){

return {

text:this.props.text

}

},

//自定义方法,设置state状态

_textChange:function(e){

this.setState({

text:e.target.value

})

},

//渲染函数,设置监听器,onChange:自定义方法

render:function(){

return React.DOM.div(null,

React.DOM.textarea({

defaultValue : this.props.text,

onChange : this._textChange

}),

React.DOM.h3(null,this.state.text.length)

)

}



});//自定义组件,一个实时计算输入字符长度的组件

调用:

1
2
3
4
5
6
var myInput_render = ReactDOM.render(

React.createElement(myIput,{text:'default'}),

document.getElementById('ex4'));

1.5 外部访问组件

1
myInout_render.setState({text:""});//外部访问组件,并改变state,不推荐!

1.6 生命周期方法:使用生命周期给我们后来方面实时修改页面数据

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
componentWillUpdate()//组件再次渲染调用,如render前调用,组件的props与state发生改变会调用

componentDidUpdate()//render执行完毕,更新的组件同步DOM后调用,不会在初始化调用

componentWillMount()//新节点插入前调用

componentWillDidMount()//新节点插入之后调用

componentWillUnmount()//组件从DOM移除调用

shouldComponentUpdate(newProps,newState)//在componentWillUpdate之前触发

{···

_log:function(Mname,args){

console.log(Mname,args);

},

componentWillUpdate:function(){

this._log('componentWillUpdate:渲染前调用|props与state改变调用',arguments)

},

componentDidUpdate:function(){

this._log('componentWillDidUpdate:render执行完,同步DOM之后调用',arguments)

},

shouldComponentUpdate:function(newProps,newState){

this._log('shouldComponentUpdate:componentWillUpdate之前触发,返回false能禁止render调用',arguments);

return true

},

componentDidMount:function(){

this._log('componentDidMount:新节点插入之后触发',arguments)

},

componentWillMount:function(){

this._log("componentWillMount:新节点插入之前触发",arguments)

},

componentWillUnmount:function(){

this._log('componentWillUpdate:组件从DOM移触发',arguments)

}

···}

1.6 mixin 简化重复的生命周期方法(其实就是一个json格式,简化了写法)

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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
var myMixin_one = {

_log:function(Mname,args){

console.log(Mname,args);

},

componentWillUpdate:function(){

this._log('componentWillUpdate:渲染前调用|props与state改变调用',arguments)

},

componentDidUpdate:function(){

this._log('componentWillDidUpdate:render执行完,同步DOM之后调用',arguments)

},

shouldComponentUpdate:function(newProps,newState){

this._log('shouldComponentUpdate:componentWillUpdate之前触发,返回false能禁止render调用',arguments);

return true

},

componentDidMount:function(){

this._log('componentDidMount:新节点插入之后触发',arguments)

},

componentWillMount:function(){

this._log("componentWillMount:新节点插入之前触发",arguments)

},

componentWillUnmount:function(){

this._log('componentWillUpdate:组件从DOM移触发',arguments)

}



};

var myMixin_two = {

proptypes:{

text:React.PropTypes.string

},

getDefaultProps:function(){

return {

text:''

}

},

getInitialState:function(){

return {

text:this.props.text

}

},

_textChange:function(e){

this.setState({

text:e.target.value

})

},

};

var myIput = React.createClass({

render:function(){

return React.DOM.div(null,

React.DOM.textarea({

defaultValue:this.state.text,

onChange:this._textChange

}),

React.DOM.h3(null,this.state.text.length)

)

},



mixins:[myMixin_one,myMixin_two]//用mixin替代周期函数与初始化方法



});




1.7 子组件:子组件从属于父组件

//定义父组件

var parCom = React.createClass({

propTypes:{

text:React.PropTypes.string

},

getDefaultProps:function(){

return {

text:''

}

},



_textChange:function(e){

this.setState({

text:e.target.value

})

},

getInitialState:function(){

return {

text:this.props.text

}

},

render:function(){

var counter = null;

if(this.state.text.length>0){

//初始化子组件

counter = React.DOM.h3(null,React.createElement(sonCom,{

count:this.state.text.length

}))

//父组件,渲染时候,检测条件,是否渲染子组件

//如果父组件的text属性值大于0,渲染子组件

}

return React.DOM.div(null,

React.DOM.textarea({

defaultValue:this.state.text,

onChange:this._textChange

}),counter//引用子组件

)

}

});

//定义子组件

var sonCom = React.createClass({

name:'Counter',

propTypes:{

count:React.PropTypes.number.isRequired,

},

render:function(){

return React.DOM.span(null,this.props.count);

}

})

//调用

1
ReactDOM.render(React.createElement(parCom,{text:''}),document.getElementById('ex5'))

下面是一个完整的表格React应用demo过程部分代码来自:《React快速上手》

1.8 Excel组件

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
var Excel = React.createClass({

displayName:'Excel',

getInitialState:function(){

return {

data:this.props.initialdata

}

},

render:function(){

return React.DOM.table({className:"table"+" "+"table-bordered"},//多个类用拼接

React.DOM.thead(null,

React.DOM.tr(null,

this.props.headers.map(function(title,idx){

//map()方法,将数组的值传入回调函数

// arr.map(function(currentValue,index,arr),thisValue)

//参数说明 function(currentValue,index,arr) 必须,函数,数组中的每个元素都会执行这个函数函数参数 函数参数 currentValue 必须 当前元素值 index 可选 当前元素的索引值 arr 可选 当前元素属于的数组对象。

return React.DOM.th({key:idx},title);

})

)

),

React.DOM.tbody(null,this.state.data.map(function(row,idx){

return React.DOM.tr({key:idx},

row.map(function(cell,idx){

return React.DOM.td({key:idx},cell)

})

)

}))

);

}

});



var headers = ["book","author","lanage","sales"];

var data = [["math","jack","en","23"],["english","alice","23","en"]];



ReactDOM.render(React.createElement(Excel,{



headers:headers,

initialdata:data

}),document.getElementById('ex6'));

效果图:

实现排序:arr.sort(回调函数)

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
_sort:function(e){

var column = e.target.cellIndex;//当前选中的列索引

var data = this.state.data.slice();//对数组的复制

data.sort(function(a,b){

console.log(a[column],b);

return a[column]>b[column] ? 1: -1;

});

console.log(e.target.cellIndex);

//更新状态

this.setState({

data:data

})
添加监听器:

React.DOM.thead({onClick:this._sort},
实现编辑:这里用了bootstrap的框架,很好看啊

getInitialState:function(){

return {

data:this.props.initialdata,

edit:null

};

},

_edit:function(e){

this.setState({

edit:{

row:parseInt(e.target.dataset.row,10),

cell:e.target.cellIndex

}

});

},

·····

React.DOM.tbody({

onDoubleClick:this._edit},this.state.data.map(function(row,rowidx){

return React.DOM.tr({key:rowidx},

row.map(function(cell,idx){

var content = cell;

var edit = this.state.edit;

if(edit && edit.row === rowidx && edit.cell === idx){

content = React.DOM.form({

onSubmit:this._save

},React.DOM.input({

type:"text",

className:"form-control",

defaultValue:content

}))

}

return React.DOM.td({key:idx,"data-row":rowidx},content)

},this)

)

},this))

效果图:

实现保存:回车保存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
_save:function(e){

e.preventDefault();//阻止默认事件,防止重新加载

var input = e.target.firstChild;//引用输入框值

var data = this.state.data.slice();//复制一份数据修改

data[this.state.edit.row][this.state.edit.cell] = input.value;//修改指定行指定cell数据

this.setState({

data:data,//刷新数据

edit:null//去除input

})



},

效果图:

完整源代码:有注释可以研究一下!

1
2
3
4
5
6
7
8
9
10
11
12
<div class="panel panel-primary">

<div class="panel-heading text-center">

React Excel (单击列标题排序,双击单元格编辑,回车确认)

</div>

<div id="ex6" class="panel-body"></div>

</div>

//定义表格组件

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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
var Excel = React.createClass({

displayName:'Excel',//内部为React标记此组件名

getInitialState:function(){

return {

data:this.props.initialdata,//初始化数据

edit:null//是否开启编辑

};

},

//排序方法

_sort:function(e){

var column = e.target.cellIndex;//获取列索引

var data = this.state.data.slice();//复制数组

//数组排序方法,传入,回调函数(比较两个数据,返回真假)

data.sort(function(a,b){

console.log(a[column],b);

return a[column]>b[column] ? 1: -1;

});//每次排序后,刷新状态

console.log(e.target.cellIndex);

this.setState({

data:data

})

},

//编辑方法

_edit:function(e){

this.setState({

edit:{

row:parseInt(e.target.dataset.row,10),//强制转整形,通过自定义的dataset-row

cell:e.target.cellIndex//获取row与cell即每个单元格的定位

}

});

},

//提交保存

_save:function(e){

e.preventDefault();//阻止浏览器默认事件

var input = e.target.firstChild;//获取输入框值

var data = this.state.data.slice();

data[this.state.edit.row][this.state.edit.cell] = input.value;//修改指定单元格数据

this.setState({

data:data,

edit:null//去除输入框

})



},

render:function(){

return React.DOM.table({className:"table"+" "+"table-bordered"},

React.DOM.thead({

onClick:this._sort},//添加单击监听器

React.DOM.tr(null,

this.props.headers.map(function(title,idx){

return React.DOM.th({key:idx},title);

})

)

),

React.DOM.tbody({//添加双击监听器

onDoubleClick:this._edit},this.state.data.map(function(row,rowidx){

return React.DOM.tr({key:rowidx},

row.map(function(cell,idx){

var content = cell;

var edit = this.state.edit;//获取edit数据,判断覆盖单元格并生成input的定位

if(edit && edit.row === rowidx && edit.cell === idx){

content = React.DOM.form({

onSubmit:this._save//表单事件

},React.DOM.input({

type:"text",

className:"form-control",

style:{

maxWidth:"100px"

},

defaultValue:content

}))

}

return React.DOM.td({key:idx,"data-row":rowidx},content)

},this)

)

},this))

);

}

});



var headers = ["图书","作者","语言","售价"];

var data = [["数学书","李","中文","23元"],["英语书","爱丽丝","英文","26元"],["PHP编程","鲍勃","英文","21元"],["React笔记","bore","中文","26元"]];



ReactDOM.render(React.createElement(Excel,{



headers:headers,

initialdata:data

}),document.getElementById('ex6'));

现在用JSX简化表格组件

1.9 JSX实现Excel组件:JSX在学习时候,引入browser.js来转译jsx到js,实际使用要先转译再使用!

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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8" />

<title>React笔记</title>

<script src="react.js"></script>

<script src="react-dom.js"></script>

<script src="browser.min.js"></script>JSX转换插件

<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.js"></script>

<script src="https://cdn.bootcss.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>

<link href="https://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">

<style type="text/css">



.border{

border: 1px solid red;margin: 10px;padding: 10px;

}

.borderIn{

border: 1px solid blue;margin: 10px;padding: 10px;

}



</style>

</head>

<body>

<div class="panel panel-primary">

<div class="panel-heading text-center">

React Excel (单击列标题排序,双击单元格编辑,回车确认)

</div>

<div id="ex6" class="panel-body"></div>

</div>



<script type="text/babel">



var Excel = React.createClass(

{

/*定义内部组件名字*/

displayName:'Excel',

/*定义接收属性*/

propTypes:{

headers:React.PropTypes.arrayOf(React.PropTypes.string),/*初始化头部,接收数组,数组值为string*/

initialdata:React.PropTypes.arrayOf(React.PropTypes.arrayOf(React.PropTypes.string))

},/*初始化内容,数据接收数组,值为string*/

/*初始化属性*/

getInitialState:function(){

return {

data:this.props.initialdata,/*初始化组件内部属性属性值*/

sortby:null,/*排序*/

edit:null,/*编辑*/

search:false,/*搜索*/

desc:false

};

},

/*排序方法*/

_sort:function(e) {

var cloumn = e.target.cellIndex;/*获取触发所在的列索引*/

console.log(e.target.cellIndex);

var data = this.state.data.slice();/*复制数据操作*/

var desc = this.state.desc;

data.sort(function(a,b) {

console.log(a);

console.log(b);

return desc ? (a[cloumn]>b[cloumn] ? 1 : -1) : (a[cloumn]<b[cloumn] ? 1 : -1);/*判断排序*/

});

/*更新状态*/

if(desc == false){

desc = true;

}else{

desc = false;

}

this.setState({

data:data,

desc:desc

});

},

/*编辑方法*/

_edit:function(e){

this.setState({

edit:{

row:parseInt(e.target.dataset.row,10),

cell:e.target.cellIndex,

}

});/*刷新状态:定位点击单元格位置*/

},

/*保存方法*/

_save:function(e){

e.preventDefault();/*阻止默认网页重载*/

var input = e.target.firstChild;/*获取单元格文本*/

var data = this.state.data.slice();/*复制数据*/

data[this.state.edit.row][this.state.edit.cell] = input.value;/*将单元格文本存储到指定data位置*/

this.setState({

data:data,

edit:null,});

},

_json:function(){



},

_csv:function(){



},

_search:function(){



},

/*渲染工具栏*/

render_head:function(){

return (

<div className="container-fluid">

<div className="row">

<div className="col-xs-2" >

<button type="button" onClick={this._search} style={{margin:"5px",width:"100%"}} className="btn btn-primary" >搜索</button>

</div>

<div className="col-xs-2">

<button type="button" onClick={this._json} style={{margin:"5px",width:"100%"}} className="btn btn-default">导出JSON</button>

</div>

<div className="col-xs-2">

<button type="button"onClick={this._csv} style={{margin:"5px",width:"100%"}} className="btn btn-success">导出CSV</button>

</div>

</div>

</div>

)



},

/*渲染表格*/

render:function(){

return (/*这个括号不可以换行。。。*/

<div>/*不乐意返回多个顶级节点,用div包裹*/

{this.render_head()}

<table className="table table-bordered">

<thead onClick={this._sort}>

<tr>{

/*在{}中编写JS*/

this.props.headers.map(function(title,idx){

return <th key={idx}>{title+=this.state.desc ? '\u2193' : '\u2191'}</th>;



},this)

}

</tr>

</thead>

<tbody onDoubleClick={this._edit}>

{

this.state.data.map(function(row,rowidx){

return (

<tr key={rowidx}>

{

row.map(function(cell,idx){

var content = cell;

var edit = this.state.edit;

if(edit && edit.row === rowidx && edit.cell === idx){

content = (

<form onSubmit={this._save}>

<input style={{maxWidth:"100px"}} type="text" className="form-control" defaultValue={cell} />

</form>);

}

return <td key={idx} data-row={rowidx}>{content}</td>

},this)

}

</tr>

);

},this)

}

</tbody>

</table>

</div>

);



},



}

);



var headers = ["图书","作者","语言","售价"];

var data = [["数学书","李","中文","23元"],

["英语书","爱丽丝","英文","26元"],

["PHP编程","鲍勃","英文","21元"],

["React笔记","bore","中文","26元"]];



ReactDOM.render(React.createElement(Excel,{



headers:headers,

initialdata:data

}),document.getElementById('ex6'));





</script>

</body>

</html>

暂时就这样啦。