React基础知识详细_ES6版

React基础知识

ES6版以下几点需要注意

  • 不支持 getInitialState 在设置组件初始的 state ,可在组件的 constructor 中通过 this.state 设置,也可直接作为 properties 定义
  • propTypes、defaultProps 作为 properties 定义,也可在组件外部通过键值对方式设置。
  • 不支持 mixins,可以使用高阶组件写法,或者 decorator
  • 组件的名字的首字母一定要大写

顶层API

最简单的React组件及其渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
import React, { Component } from 'react';
/**
* 最简单的一个组件
*/
class SimpleComponent extends Component {
render(){
return <div> here we go </div>;
}
}
// 我们可以看看React给我们提供了哪些顶层组件
console.log( React );
export default SimpleComponent;

react.js

React.Children: Object
React.Component: ReactComponent(props, context, updater)
React.DOM: Object
React.PropTypes: Object
React.cloneElement: (element, props, children)
React.createClass: (spec)
React.createElement: (type, props, children)
React.createFactory: (type)
React.createMixin: (mixin)
React.isValidElement: (object)

Component API

this.context: Object
this.props: Object
this.refs: Object
this.state: Object
this.setState: Object

react-dom.js

ReactDOM.findDOMNode: findDOMNode(componentOrElement)
ReactDOM.render: ()
ReactDOM.unmountComponentAtNode: (container)

jsx语法

类似 xml 的语法,用来描述组件树

1
2
3
4
<div classname="x">
<a href="#">#</a>
<component x="y">1</component>
</div>

不用JSX,用React提供的API写的话

1
2
3
4
5
6
React.createElement('div',{
className:'x'
},[
React.createElement('a',{href:'#'},'#'),
React.createElement(Component,{x:'y'},1);
]);

注释、命名、根元素个数、JSX 嵌入变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React, { Component } from 'react';
// 1. 组件命名遵循驼峰命名,首字母大写
class ComponentDemo extends Component {
render(){
{
/*
2. 这是代码注释
也可以是多行
*/
}
const name = this.props.name;
// 3. render 方法 return 回来的根元素只能是一个,超过会报错
// 4. { } 里面可以写JS代码
return (
<div>
hello, {name ? name : "我是默认的"}
</div>
);
}
}
export default ComponentDemo;

React只有一个限制, 组件只能渲染单个根节点。如果你想要返回多个节点,它们必须被包含在同一个节点里。
styles

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React, { Component } from 'react';
class StyleDemo extends Component {
render(){
// 5. 在JS文件里面给组件定义样式
var MyComponentStyles = {
color: 'blue',
fontSize: '28px'
};
return (
<div style={MyComponentStyles}>
可以直接这样写行内样式
</div>
)
}
}
export default StyleDemo;

JSX SPREAD
可以用通过 {…obj} 来批量设置一个对象的键值对到组件的属性,注意顺序

1
2
3
4
5
6
7
8
9
10
11
12
import React, { Component } from 'react';
class SpreadDemo extends Component {
componentWillMount(){
console.log(this.props);
}
render(){
return <h1> {this.props.name} is a {this.props.type} </h1>;
}
}
export default SpreadDemo;

属性名不能和 js 关键字冲突
例如:className, readOnly, htmlfor

数据流: state props propType

state && setState
用状态控制组件变化 可以把一个组件看做一个状态机, 每一次状态对应于组件的一个 ui
组件内部的状态,可以使用 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
import React, { Component } from 'react';
class StateDemo extends Component {
state = {
secondsElapsed: 0
}
tick(){
this.setState({ secondsElapsed: this.state.secondsElapsed + 1 });
}
componentDidMount(){
this.interval = setInterval( this.tick.bind(this), 1000 );
}
componentWillUnmount(){
clearInterval(this.interval);
}
render(){
return (
<div>目前已经计时:{this.state.secondsElapsed}秒</div>
)
}
}
export default StateDemo;

props
通过 this.props 可以获取传递给该组件的属性值,还可以通过定义 getDefaultProps 来指定默认属性值(这是ES5的写法,ES6定义组件的默认props可以直接写props)
下面几个是props的常用API:

  • this.props.children
  • this.props.map
  • this.props.filter

props是调用组件的时候传递进去的数据,一般用于组件树数据传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React, { Component } from 'react';
class PropsDemo extends Component {
props = {
title: '这是默认的title属性值'
}
render(){
console.log(this.props);
return <b>{this.props.title}</b>
}
}
export default PropsDemo;
// 组件调用方式
// <PropsDemo title="设置的标题" />

propTypes
通过指定 propTypes 可以校验props属性值的类型,校验可提升开发者体验,用于约定统一的接口规范。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React, { Component, PropTypes } from 'react';
class PropTypesDemo extends Component {
static propTypes = {
title: PropTypes.string.isRequired
}
props = {
title: '默认的title'
}
render(){
return <b>{this.props.title}</b>
}
}
export default PropTypesDemo;

调用API定义组件

用 React.createClass或者React.Component 定义组件时允许传入相应的配置及组件API的使用,包括组件生命周期提供的一系列钩子函数。

  • 组件初始定义
    • getDefaultProps 得到默认属性对象,这个在ES6的时候不需要这样定义
      • propTypes 属性检验规则
      • mixins 组件间公用方法
  • 初次创建组件时调用
    • getInitialState 得到初始状态对象
    • render 返回组件树. 必须设置
    • componentDidMount 渲染到 dom 树中是调用,只在客户端调用,可用于获取原生节点
  • 组件的属性值改变时调用
    • componentWillReceiveProps 属性改变调用
    • shouldComponentUpdate 判断是否需要重新渲染
    • render 返回组件树. 必须设置
    • componentDidUpdate 渲染到 dom 树中是调用, 可用于获取原生节点
  • 销毁组件
    • componentWillUnmount 组件从 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
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    import React, { Component } from 'react';
    class LifeCycle extends Component {
    Props = {
    value: '开始渲染'
    }
    componentWillReceiveProps(nextProps){
    console.log('componentWillReceiveProps');
    this.setState({
    value: nextProps.value
    });
    }
    shouldComponentUpdate(nextProps,nextState){
    console.log('shouldComponentUpdate');
    return true;
    }
    componentWillUpdate(nextProps,nextState){
    console.log('componentWillUpdate');
    }
    componentWillMount(){
    console.log('componentWillMount');
    }
    render() {
    console.log('render');
    return <span>{this.props.value}</span>
    }
    componentDidMount() {
    console.log('componentDidMount');
    }
    componentDidUpdate(prevProps,prevState) {
    console.log('componentDidUpdate');
    }
    componentWillUnmount(prevProps,prevState) {
    console.log('componentWillUnmount');
    }
    }
    export default 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
import React, { Component } from 'react';
import LifeCycleDemo from './LifeCycleDemo';
class DestroyComponent extends Component {
state = {
value:1,
destroyed:false
}
increase = () => {
this.setState({
value: this.state.value + 1
});
}
destroy = () => {
this.setState({
destroyed: true
});
}
render() {
if(this.state.destroyed){
return null;
}
return <div>
<p>
<button onClick={this.increase}>每次加1</button>
<button onClick={this.destroy}>干掉这两个按钮</button>
</p>
<LifeCycleDemo value={this.state.value}/>
</div>;
}
}
export default DestroyComponent;

回顾组件的渲染过程

  • 创建-》渲染-》销毁
    • getDefaultProps()
    • getInitialState()
    • componentWillMount()
    • render()
    • componentDidMount()
    • componentWillUnmount()
  • 更新组件
    • componentWillReceiveProps()
    • shouldComponentUpdate()
    • componentWillUpdate()
    • render()
    • componentDidUpdate()

使用ref对操作DOM

  • ReactDOM.findDOMNode
  • this.refs.xxx

获取DOM后可以方便结合现有非 react 类库的使用,通过 ref/refs 可以取得组件实例,进而取得原生节点,不过尽量通过 state/props 更新组件,不要使用该功能去更新组件的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
import React, { Component } from 'react';
import ReactDOM, { findDOMNode } from 'react-dom';
class HandleDOMComponent extends Component {
componentDidMount(){
// 两种方式都可以获取到元素
let ele = findDOMNode(this.refs.content);
let ele2 = this.refs.content;
// 如果想用 jquery,那么这是个好时机
console.log( ele );
console.log( ele.innerHTML );
console.log( ele2.innerHTML );
}
render(){
return (
<div>
<h3>来吧,一起操作DOM</h3>
<div ref='content'>这是我DOM元素里面的内容</div>
</div>
);
}
}
export default HandleDOMComponent;

再看一个有趣的例子

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
import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
class Refs extends Component {
state = {
red: 0,
green: 0,
pink: 0
}
update = (e) => {
this.setState({
red: findDOMNode(this.refs.red).value,
green: findDOMNode(this.refs.green).value,
pink: findDOMNode(this.refs.pink).value
})
}
render(){
return (
<div>
<Slider ref="red" update={this.update} />
{this.state.red}
<br />
<Slider ref="green" update={this.update} />
{this.state.green}
<br />
<Slider ref="pink" update={this.update} />
{this.state.pink}
</div>
)
}
}
class Slider extends Component {
render(){
return (
<input type="range"
min="0"
max="255"
onChange={this.props.update} />
)
}
}
export default Refs;

事件event

可以通过设置原生 dom 组件的 onEventType 属性来监听 dom 事件,例如 onClick, onMouseDown,在加强组件内聚性的同时,避免了传统 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
'use strict';
import React, { Component } from 'react';
class HandleEvent extends Component {
state = { liked: false }
handleClick = (event) => {
this.setState({liked: !this.state.liked});
}
render() {
let text = this.state.liked ? '喜欢' : '不喜欢';
return (
<p onClick={this.handleClick}>
我 {text} 你.
</p>
);
}
}
export default HandleEvent;

注意:事件回调函数参数为标准化的事件对象,可以不用考虑 IE

8. 组件的组合

  • 受限组件 && 非受限组件

受限组件示例:

1
2
3
render() {
return <input type="text" value="Hello!" />;
}

非受限组件示例:

1
2
3
render() {
return <input type="text" value="Hello!" />;
}

  • 使用自定义的组件

    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
    'use strict';
    import React, { Component } from 'react';
    class ComponentA extends Component {
    render() {
    return <a href='#'>我是组件A<br/></a>
    }
    }
    class ComponentB extends Component {
    render() {
    return <a href='#'>我是组件B</a>
    }
    }
    class SelfCreateComponent extends Component {
    render() {
    return (
    <i>
    <ComponentA />
    <ComponentB />
    </i>
    );
    }
    }
    export default SelfCreateComponent;
  • 组合 CHILDREN
    自定义组件中可以通过 this.props.children 访问自定义组件的子节点

    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
    'use strict';
    import React, { Component } from 'react';
    // 定义一个组件,通过React.Children 拿到组件里面的子元素
    class ListComponent extends Component {
    render(){
    return <ul>
    {
    React.Children.map( this.props.children, function(c){
    return <li>{c}</li>;
    })
    }
    </ul>
    }
    }
    class UseChildrenComponent extends Component {
    render(){
    return (
    <ListComponent>
    <a href="#">Facebook</a>
    <a href="#">Google</a>
    <a href="#">SpaceX</a>
    </ListComponent>
    )
    }
    }
    export default UseChildrenComponent;

form表单操作

  • React表单组件和 html 的不同点
  • value/checked 属性设置后,用户输入无效
  • textarea 的值要设置在 value 属性
  • <Textarea name="description" value="This is a description." />
  • select 的 value 属性可以是数组,不建议使用 option 的 selected 属性
  • <select multiple={true} value={['B', 'C']}> <option value="A">Apple</option> <option value="B">Banana</option> <option value="C">Cranberry</option> </select>
  • input/textarea 的 onChange 用户每次输入都会触发(即使不失去焦点)
  • radio/checkbox/option 点击后触发 onChange
  • 综合表达组件示例
    定义复选框组件Checkboxes
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import React, { Component } from 'react';
    class Checkboxes extends Component {
    render(){
    return <span>
    A
    <input onChange={this.props.handleCheck} name="goodCheckbox" type="checkbox" value="A"/>
    B
    <input onChange={this.props.handleCheck} name="goodCheckbox" type="checkbox" value="B" />
    C
    <input onChange={this.props.handleCheck} name="goodCheckbox" type="checkbox" value="C" />
    </span>
    }
    }
    export default Checkboxes;

定义单选框按钮组RadioButtons

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React, { Component } from 'react';
class RadioButtons extends Component {
saySomething(){
alert("我是一个很棒的单选框按钮组");
}
render(){
return <span>
A
<input onChange={this.props.handleRadio} name="goodRadio" type="radio" value="A"/>
B
<input onChange={this.props.handleRadio} name="goodRadio" type="radio" defaultChecked value="B"/>
C
<input onChange={this.props.handleRadio} name="goodRadio" type="radio" value="C"/>
</span>
}
}
export default RadioButtons;

FormApp组件集成两个组件并处理表单逻辑

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
'use strict';
import React, { Component } from 'react';
import Checkboxes from './Checkboxes';
import RadioButtons from './RadioButtons';
class FormApp extends Component {
state = {
inputValue: '请输入...',
selectValue: 'A',
radioValue:'B',
checkValues:[],
textareaValue:'请输入...'
}
handleSubmit = (e) => {
e.preventDefault();
let formData = {
input: this.refs.goodInput.value,
select: this.refs.goodSelect.value,
textarea: this.refs.goodTextarea.value,
radio: this.state.radioValue,
check: this.state.checkValues,
}
alert('您即将提交表单')
console.log('你提交的数据是:')
console.log(formData);
}
handleRadio = (e) => {
this.setState({
radioValue: e.target.value,
})
}
handleCheck = (e) => {
let checkValues = this.state.checkValues.slice();
let newVal = e.target.value;
let index = checkValues.indexOf(newVal);
if( index == -1 ){
checkValues.push( newVal )
}else{
checkValues.splice(index,1);
}
this.setState({
checkValues: checkValues,
})
}
render(){
return <form onSubmit={this.handleSubmit}>
<h3> 请输入内容 </h3>
<input ref="goodInput" type="text" defaultValue={this.state.inputValue }/>
<br/>
<h3> 请选择 </h3>
<select defaultValue={ this.state.selectValue } ref="goodSelect">
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
<option value="D">D</option>
<option value="E">E</option>
</select>
<br/>
<h3> 单项选择 </h3>
<RadioButtons ref="goodRadio" handleRadio={this.handleRadio} />
<br/>
<h3> 多选按钮 </h3>
<Checkboxes handleCheck={this.handleCheck} />
<br/>
<h3> 反馈建议 </h3>
<textarea defaultValue={this.state.textareaValue} ref="goodTextarea"></textarea>
<br/>
<button type="submit">确认提交</button>
</form>
}
}
export default FormApp;

10. mixin共享

mixin 是一个普通对象,通过 mixin 可以在不同组件间共享代码,使你的React程序变得更为可重用。

注意,ES6语法不支持mixin写法,而是可以通过decorator去实现代码共享,这里使用ES5语法做示例说明。

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
import React from 'react';
var SetIntervalMixin = {
componentWillMount: function() {
this.intervals = [];
},
setInterval: function() {
this.intervals.push(setInterval.apply(null, arguments));
},
componentWillUnmount: function() {
this.intervals.forEach(clearInterval);
}
};
var MixinDemo = React.createClass({
// Use the mixin
mixins: [SetIntervalMixin],
getInitialState: function() {
return {seconds: 0};
},
componentDidMount: function() {
// Call a method on the mixin
this.setInterval(this.tick, 1000);
},
tick: function() {
this.setState({seconds: this.state.seconds + 1});
},
render: function() {
return (
<p>
计时器已经运行了: {this.state.seconds} 秒.
</p>
);
}
});
export default MixinDemo;

那么,接下来,我们用high-order component的方式来实现mixin:

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
import React, { Component } from 'react';
let Mixin = MixinComponent => class extends Component {
constructor() {
super();
this.state = { val: 0 };
this.update = this.update.bind(this);
}
update(){
this.setState({val: this.state.val + 1});
}
componentWillMount(){
console.log('will mount...')
}
render(){
return (
<MixinComponent
update={this.update}
{...this.state}
{...this.props}
/>
)
}
componentDidMount(){
console.log('mounted...')
}
}
const Button = (props) => {
return (
<button onClick={props.update}>
{props.txt} - {props.val}
</button>
)
}
const Label = (props) => {
return (
<label onMouseMove={props.update}>
{props.txt} - {props.val}
</label>
)
}
let ButtonMixed = Mixin(Button);
let LabelMixed = Mixin(Label);
class Mixins extends Component {
render(){
return (
<div>
<ButtonMixed txt="button" />
<LabelMixed txt="label" />
</div>
)
}
}
export default Mixins;

---此文摘抄自学习珠峰郭永峰老师的React
帅的人已经打赏~!