# 5. React实战
# 第一部分 初识React
# 第二章 : 我们的第一个组件
- 创建
React
元素
ReactDOM.render( ReactElement element, DOMElement container, [function callback]) -> ReactComponent
React DOM
需要一个ReactElement
类型的元素和一个DOM
元素。我们已经创建了一个能够使用的有效DOM
元素,现在需要创建一个React
元素。
TIP
定义React元素是React中轻量,无状态,不可变的基础类型。React
元素有ReactComponent-Element
和ReactDOMElement
两种类型。ReactDOMElement
是DOM
元素的虚拟表示。ReactComponentElement
引用了对应着React
组件的一个函数或类。
React.createElement
被用来创建React
元素——想到了吧!让我们看看它的函数签名,了解应该如何使用它!
React.createElement(String/ReactClass type, [object props], [children...]) -> React Element
TIP
React.createElement
接收字符串或组件(要么是扩展了React.Component
的类,要么是一个函数)、属性(props
)对象和子元素集合(children
)并返回一个React
元素。记住,React
元素是你想让React
渲染的东西的轻量级表示。它可以表示一个DOM
元素或者另一个React
组件。
基本指令:
type
——可以传入一个表示要创建的HTML
元素标签名的字符串("div"、"span"、"a"
等)或一个React
类props——properties
(属性)的缩写。props
对象提供了一种方法,指定HTML
元素上会定义哪些属性(如果是在ReactDOMElement
的上下文中)或组件类的实例可以使用哪些属性。children…
——还记得我是怎么说React
组件是可组合的吗?这就是能够进行组合的所在。children…
是type
和props
之后传入的所有参数,它让使用者能够进行嵌套、排序,甚至进一步嵌套其他React
元素。
import React from "react";
import { render } from "react-dom";
const node = document.getElementById("root");
const root = React.createElement(
"div",
{},
React.createElement(
"h1",
{},
"Hello, world!",
React.createElement(
"a",
{ href: "mailto:mark@ifelse.io" },
React.createElement("h1", {}, "React In Action"),
React.createElement("em", {}, "...and now it really is!")
)
)
);
render(root, node);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
现在使用嵌套可以更好地理解
React.createElement
做了什么,并且在开始大量使用React.createElement
时,使用嵌套可能有助于你欣赏JSX
。
- 创建
React
组件
我们可以使用函数和
JavaScript
类创建两种基本类型的组件。
- 创建
React
类
class MyReactClassComponent extends Component {
render() {
}
}
2
3
4
5
React.Component
创建组件是通过声明一个继承自React.Component
抽象基类的JavaScript
类来实现的。这个继承类通常需要至少定义一个render
方法,这个render
方法会返回单个React
元素或是一个React
元素的数组。render
方法需要只返回一个React
元素。从这一点看来,render
方法与React
元素的创建方法相似——它们可以嵌套但最高层只有一个节点。然而,与React
元素不同的是,React
类的render
方法可以访问内嵌数据(持久化的内部组件状态),以及组件方法和继承自React.Component
抽象基类的其他方法。
React
的状态React
中,那些通过扩展React.Component
并作为JavaScript
类创建的组件可能既有可变状态也有不可变状态,而基于函数创建的组件(无状态函数组件)则只能访问不可变状态(属性)。
TIP
那些继承自React.Component
的组件,可以通过类实例的this.state
属性访问可变状态。而不可变状态则是通过this.props
进行访问的。
this.setState
接收一个用来更新状态的更新器函数,而且this.setState
不返回任何东西
setState( function(prevState, props) -> nextState, callback) -> void
this.setState
接收一个返回对象的更新器函数,该对象会与状态进行浅合并。事件与React
如何协同工作?React
实现了一个合成事件系统作为虚拟DOM
的一部分,它会将浏览器中的事件转换为React
应用的事件。可以设置响应浏览器事件的事件处理器,就像通常用JavaScript
做的那样。一个区别是React
的事件处理器是设置在React
元素或组件自身之上的(而不是用addEventListener
)。可以用来自这些事件(输入框的文本、单选按钮的值或事件的目标)的数据更新组件的状态。
class CreateComment extends React.Component {
constructor(props) {
super(props);
this.state = {
content: "",
user: "林嘉恒"
};
this.handleUserChange = this.handleUserChange.bind(this);
this.handleTextChange = this.handleTextChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleUserChange(event) {
const val = event.target.value;
this.setState((prevState, props) => ({
user: val
}));
}
handleTextChange(event) {
const val = event.target.value;
this.setState({
content: val
});
}
handleSubmit(event) {
event.preventDefault();
this.setState(() => ({
user: "",
content: ""
}));
}
render() {
return React.createElement(
"form",
{
className: "createComment",
onSubmit: this.handleSubmit
},
React.createElement("input", {
type: "text",
placeholder: "Your name",
value: this.state.user,
onChange: this.handleUserChange
}),
React.createElement("input", {
type: "text",
placeholder: "Thoughts?",
value: this.state.content,
onChange: this.handleTextChange
}),
React.createElement("input", {
type: "submit",
value: "Post"
})
);
}
}
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
如果定义了一个组件方法而它却不工作,你需要确定已经正确地绑定了方法
// 一个对属性进行验证,更新状态并能够添加新评论的组件:
import React, { Component } from "react";
import { render } from "react-dom";
import PropTypes from "prop-types";
const node = document.getElementById("root");
const data = {
post: {
id: 123,
content:
"What we hope ever to do with ease, we must first learn to do with diligence. — Samuel Johnson",
user: "Mark Thomas"
},
comments: [
{
id: 0,
user: "David",
content: "such. win."
},
{
id: 1,
user: "Haley",
content: "Love it."
},
{
id: 2,
user: "Peter",
content: "Who was Samuel Johnson?"
},
{
id: 3,
user: "Mitchell",
content: "@Peter get off Letters and do your homework"
},
{
id: 4,
user: "Peter",
content: "@mitchell ok :P"
}
]
};
class Post extends Component {
constructor(props) {
super(props);
}
render() {
return React.createElement(
"div",
{
className: "post"
},
React.createElement(
"h2",
{
className: "postAuthor",
id: this.props.id
},
this.props.user,
React.createElement(
"span",
{
className: "postBody"
},
this.props.content
),
this.props.children
)
);
}
}
Post.propTypes = {
user: PropTypes.string.isRequired,
content: PropTypes.string.isRequired,
id: PropTypes.number.isRequired
};
class Comment extends Component {
constructor(props) {
super(props);
}
render() {
return React.createElement(
"div",
{
className: "comment"
},
React.createElement(
"h2",
{
className: "commentAuthor"
},
this.props.user,
React.createElement(
"span",
{
className: "commentContent"
},
this.props.content
)
)
);
}
}
Comment.propTypes = {
id: PropTypes.number.isRequired,
content: PropTypes.string.isRequired,
user: PropTypes.string.isRequired
};
class CreateComment extends Component {
constructor(props) {
super(props);
this.state = {
content: "",
user: ""
};
this.handleUserChange = this.handleUserChange.bind(this);
this.handleTextChange = this.handleTextChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleUserChange(event) {
const val = event.target.value;
this.setState(() => ({
user: val
}));
}
handleTextChange(event) {
const val = event.target.value;
this.setState({
content: val
});
}
handleSubmit(event) {
event.preventDefault();
this.props.onCommentSubmit({
user: this.state.user.trim(),
content: this.state.content.trim()
});
this.setState(() => ({
user: "",
content: ""
}));
}
render() {
return React.createElement(
"form",
{
className: "createComment",
onSubmit: this.handleSubmit
},
React.createElement("input", {
type: "text",
placeholder: "Your name",
value: this.state.user,
onChange: this.handleUserChange
}),
React.createElement("input", {
type: "text",
placeholder: "Thoughts?",
value: this.state.content,
onChange: this.handleTextChange
}),
React.createElement("input", {
type: "submit",
value: "Post"
})
);
}
}
CreateComment.propTypes = {
onCommentSubmit: PropTypes.func.isRequired,
content: PropTypes.string
};
class CommentBox extends Component {
constructor(props) {
super(props);
this.state = {
comments: this.props.comments
};
this.handleCommentSubmit = this.handleCommentSubmit.bind(this);
}
handleCommentSubmit(comment) {
const comments = this.state.comments;
comment.id = Date.now();
const newComments = comments.concat([comment]);
this.setState({
comments: newComments
});
}
render() {
return React.createElement(
"div",
{
className: "commentBox"
},
React.createElement(Post, {
id: this.props.post.id,
content: this.props.post.content,
user: this.props.post.user
}),
this.state.comments.map(function(comment) {
return React.createElement(Comment, {
key: comment.id,
id: comment.id,
content: comment.content,
user: comment.user
});
}),
React.createElement(CreateComment, {
onCommentSubmit: this.handleCommentSubmit
})
);
}
}
CommentBox.propTypes = {
post: PropTypes.object,
comments: PropTypes.arrayOf(PropTypes.object)
};
render(
React.createElement(CommentBox, {
comments: data.comments,
post: data.post
}),
node
);
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
jsx
创建组件 上面创建组件的方式用jsx
来重构:
// Post组件
class Post extends Component {
render() {
return (
<div className="post">
<h2 className="postAuthor">{this.props.user}</h2>
<span className="postBody">{this.props.content}</span>
{this.props.children}
</div>
);
}
}
// Comment组件
class Comment extends Component {
render() {
return (
<div className="comment">
<h2 className="commentAuthor">{this.props.user + " : "}</h2>
<span className="commentContent">{this.props.content}</span>
</div>
);
}
}
class CreateComment extends Component {
constructor(props) {
super(props);
this.state = {
content: "",
user: ""
};
this.handleUserChange = this.handleUserChange.bind(this);
this.handleTextChange = this.handleTextChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleUserChange(event) {
this.setState({
user: event.target.value
});
}
handleTextChange(event) {
this.setState({
content: event.target.value
});
}
handleSubmit(event) {
event.preventDefault();
this.props.onCommentSubmit({
user: this.state.user.trim(),
content: this.state.content.trim()
});
this.setState({
user: "",
content: ""
});
}
render() {
return (
<form onSubmit={this.handleSubmit} className="createComment">
<input
value={this.state.user}
onChange={this.handleUserChange}
placeholder="Your name"
type="text"
/>
<input
value={this.state.content}
onChange={this.handleTextChange}
placeholder="Thoughts?"
type="text"
/>
<button type="submit">Post</button>
</form>
);
}
}
class CommentBox extends Component {
constructor(props) {
super(props);
this.state = {
comments: this.props.comments
};
this.handleCommentSubmit = this.handleCommentSubmit.bind(this);
}
handleCommentSubmit(comment) {
const comments = this.state.comments;
comment.id = Date.now();
const newComments = comments.concat([comment]);
this.setState({
comments: newComments
});
}
render() {
return (
<div className="commentBox">
<Post
id={this.props.post.id}
content={this.props.post.content}
user={this.props.post.user}
/>
{this.state.comments.map(function(comment) {
return (
<Comment
key={comment.id}
content={comment.content}
user={comment.user}
/>
);
})}
<CreateComment onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
}
// 绑定到node节点下
render(<CommentBox comments={data.comments} post={data.post} />, node);
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