React 学习笔记

学习官方文档的一些总结和笔记

一 基础语法

1
2
3
4
5
6
7
8
9
<body>
<div id="root"></div>
<script type="text/babel">
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<div id="root"></div>
<script type="text/babel">
var names = ['react', 'javascript', 'html'];
ReactDOM.render(
<div>
{
names.map(function (name) {
return <div>Hello, {name}!</div>
})
}
</div>,
document.getElementById('root')
);
</script>
</body>

上面就是简单的JSX语法。其实就是将html语法和和JavaScript语法混合在一起。解析器解析的时候遇见 < 开头的就用html规则解析,遇到代码块 {} 就用JavaScript 规则解析。React 官方推荐我们使用JSX语法,只是推荐,我们也可以使用纯的JavaScript代码写也是可以的,代码如下,只是使用JSX语法,组件的结构和组件的关系看上去比较清楚。使用JSX的时候需要指定typetext/babel,详细的JSX语法可以参考官方文档 https://facebook.github.io/react/docs/introducing-jsx.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--不使用JSX语法-->
<body>
<div id = "root"/>
<script type="text/babel">
class Hello extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.toWhat}`);
}
}

ReactDOM.render(
React.createElement(Hello, {toWhat: 'World'}, null),
document.getElementById('root')
);
</script>
</body>

二 Component

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<div id="root"></div>
<script type="text/babel">
//通过React.createClass创建自定义组件
var HelloWord = React.createClass({
render: function() {
//只能包含一个顶层标签
return <h1>Hello {this.props.name}</h1>;
}
});
ReactDOM.render(
//把组件渲染到页面中
<HelloWord name="Word" />,
document.getElementById('root')
);
</script>
</body>

上面就是一个React简单的组件。HelloWord是一个组件类,组件类的名称首字母必须大写,自定义的组件必须通过React.render方法进行解析,在其他html标签中使用是没有效果的。this.props 是获取当前自定义组件的属性名称,获取对应的值就是加上对应的属性名称,比如this.props.name 。有一点例外就是 this.props.children 这个不是获取属性名称为children的值,它是获取组件里面的所有子节点对象。具体怎么使用可以查看官网的API https://facebook.github.io/react/docs/react-api.html#react.children我们使用组件属性的时候和普通的html标签没有什么区别。但是我们的属性可以指定任意类型。代码如下

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
<body>
<div id="root"></div>
<script type="text/babel">
var data = 1106;
var CustomButton = React.createClass({
//设置属性的类型
propTypes: {
title: React.PropTypes.string,
},
//设置属性的默认值
getDefaultProps : function () {
return {
title : 'Hello World'
};
},
render: function() {
return <h1> {this.props.title} </h1>;
}
});

ReactDOM.render(
<CustomButton title={data} />,
document.getElementById('root')
);
</script>
</body>

上面的代码我们给自己的组件CustomButton类定义了propTypes,指定属性名称title的类型为string,然后我们传入一个int类型的值给title,这样我们的console会提醒你 Warning: Failed propType: Invalid prop title of type number supplied to CustomButton, expected string. 这样就验证了属性的类型,在着我们可以指定React.PropTypes.string.isRequired来要求我们title这个属性是必须的,没有值的话会报错。以此来达到一些限制。我们制定了属性的类型之后,也可以设置属性的默认值,通过getDefaultProps 去设置。更多的具体属性类型可以看官方文档 https://facebook.github.io/react/docs/react-api.html#typechecking-with-proptypes

三 Component State

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
<body>
<div id="root"></div>
<script type="text/babel">
var CustomButton = React.createClass({
//初始化myKey状态的值
getInitialState: function() {
return {myKey: false};
},
//定义组件的点击事件
handleClick: function(event) {
//设置状态liked的值
this.setState({myKey: !this.state.myKey});
},
render: function() {
var text = this.state.myKey ? 'myKey' : 'haven\'t myKey';
return (
<p onClick={this.handleClick}>
You {text} this. Click to toggle.
</p>
);
}
});

ReactDOM.render(
<CustomButton />,
document.getElementById('root')
);
</script>
</body>

上面的代码就是一个简单的组件状态的用法。在点击文本的时候设置不同的状态值进而渲染出不同的内容。只要执行setState的时候React会去重新计算DOM,渲染变化的值。除非在shouldComponentUpdate方法中返回false,个人觉得React最好的地方就是这个状态的转换,用户之间的交互更加流畅。还有就是setState是异步的,this.setState会调用render方法,但并不会立即改变state的值,state是在render方法中赋值的。所以执行this.setState()后立即获取state的值是不变的。同样的直接赋值state并不会出发更新,因为没有调用render函数。还有就是关于定义组件事件的,React中绑定事件处理器是使用驼峰命名法的方式。

四 Component Form

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
<body>
<div id="root"></div>
<script type="text/babel">
//自定义组件也可以继承React.Component
class NameForm extends React.Component {
constructor(props) {
super(props);
//构造方法初始化状态值
this.state = {value: ''};
//绑定事件的回调函数,这里是必须的。不然运行的时候会undefined
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
//定义组件事件
handleChange(event) {
this.setState({value: event.target.value});
}
//定义组件事件
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}

render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}

ReactDOM.render(
<NameForm />,
document.getElementById('root')
);
</script>
</body>

上面的代码是React处理表单,React认为表单的数据变化是用户和组件的一种交互,随着用户的输入,属性值是变化的,所以我们不能简单的用this.props去获取表单的值,因为this.props的值定义好了之后是不能变化的,而React做法是注册一个onChange事件的回调函数,通过实时的改变状态的值,进而获取和设置表单的值。而且 textarea tagsselect tag 也是这种情况。具体可以参考官方文档 https://facebook.github.io/react/docs/forms.html

五 Component Lifecycle

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
<body>
<div id="root"></div>
<script type="text/babel">
var HelloLifecycle = React.createClass(
{
//初始化属性默认值,只调用一次,实例之间共享引用
getDefaultProps:function() {
console.log('getDefaultProps,1');
return {
name1 : 'name1',
name2 : 'name2'
};
},
// 初始化每个实例特有的状态
getInitialState:function() {
console.log('getInitialState,2');
return null;
},
//组件渲染之前调用
componentWillMount:function() {
console.log('componentWillMount,3');
},
//只能访问this.props和this.state,只有一个顶层组件,不允许修改状态和DOM输出
render: function() {
console.log('component render 4');
return <p>Hello ,{this.props.name1 + " " + this.props.name2}</p>;
},
//成功render并渲染完成真实DOM之后触发,可以修改DOM
componentDidMount:function() {
console.log('componentDidMount,5');
},

//运行中的函数
//父组件修改属性触发,可以修改新属性、修改状态
//obj为接收到修改了属性的对象,对象包含了修改后的属性在里边
componentWillReceiveProps:function(obj) {
console.log("运行中:1");
console.log(obj);
},
//组件判断是否重新渲染时调用
shouldComponentUpdate: function() {
console.log("运行中:2");
return true;
},
//组件更新前调用,不能修改属性和状态
componentWillUpdate: function() {
console.log("运行中:3");

},
//组件更新后调用
//可以修改DOM
componentDidUpdate:function() {
console.log("运行中:5");
},
//销毁阶段,即把这个组件移出后执行的回调函数
//在删除组件之前进行清理操作,比如计时器和事件监听器
componentWillUnmount: function() {
alert('销毁,我是组件');
}
}
);

ReactDOM.render(
<HelloLifecycle />,
document.getElementById('root')
);
</script>
</body>

运行上面的代码可以看到console打印出了了

  1. getDefaultProps,1
  2. getInitialState,2
  3. componentWillMount,3
  4. component render,4
  5. componentDidMount,5

这个是初始化时候的生命周期。运行时候的生命周期主要就是两部分 update Unmount,分别有组件更新前,组件更新后,组件销毁前,组件销毁后。其实总的来说分三种

  1. Mounting 加载组件
  2. Updating 重新记载组件
  3. Unmounting 移除组件

针对这三种状态,React每种状态设置了willdid回调函数。分别代表前和后。具体的生命周期内容可以参考官方文档 https://facebook.github.io/react/docs/react-component.html

六 Refs and the DOM

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
<body>
<div id="root"></div>
<script type="text/babel">
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
//绑定focus回调函数
this.focus = this.focus.bind(this);
}

focus() {
// 获取输入框的焦点
this.textInput.focus();
}

render() {
return (
<div>
<input
type="text"
ref={(input) => { this.textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={this.focus}
/>
</div>
);
}
}
ReactDOM.render(
<CustomTextInput />,
document.getElementById('root')
);
</script>
</body>

React中是基于数据流的,props是父组件和子组件交互的唯一方式,但是在某些情况下,我们需要在数据流之外强制修改子组件,子组件可以使React的实例,也可以是DOM元素。针对这种情况,可以使用refs属性。当组件装载的时候,React将会使用真实的DOM元素调用ref回调函数,当组件卸载的时候,会用null对象来回调,在上面的代码中,CustomTextInput组件装载后,React会调用ref的回调函数,将真实的input元素,复制给组件里面的textInput。当我们点击button的时候,调用focus回调,获取输入框的焦点。其实React也可以通过this.refs.myRefName来获取真实的DOM对象,官方建议我们使用事件回调的给属性赋值的方式,因为this.refs.myRefName必须在组件加载好了之后才能获取到真实的DOM,为了使用更加的友好,应该抛弃这种方式。再者</font color=’red’>不可以在自定义的组件中使用ref属性,因为组件是没有具体的实例的</font>

总结

  1. React是基于状态驱使的。在整个React开发中,体会到,组件的状态贯穿着一切,一切都是基于状态驱使开发的。
  2. 仅仅只要表达出应用程序在任一个时间点应该长的样子,然后当底层的数据变了,React会自动处理所有用户界面的更新。
  3. React中数据流是单向的,从父节点传递到子节点,因而组件是简单且易于把握的,组件只需要从父节点获取props渲染即可。如果顶层组件的某 个prop改变了,React会递归地向下遍历整棵组件树,重新渲染所有使用这个属性的组件。
  4. 可以通过 this.props 来访问组件的 props ,但不能通过这种方式修改它,一个组件绝对不可以自己修改自己的 propsprops 可以是字符串,对象,事件处理器等。可以使用 PropTypes 来验证 props 的类型,推荐使用。
  5. 每一个React组件都可以拥有自己的state,其与props的区别是,state只存在于组件内部,state 的值应该通过 this.setState()修改,只要该状态被修改, render 方法就会被调用,然后导致之后的一系列变化。状态总是让组件变得更加复杂,把状态针对不同的组件独立开来,能更有利于调试。