react学习总结系列-react(二)

这篇主要写的是React中父子组件数据传递跟生命周期。

父子组件数据传递

上一篇其实有说到,组件跟组件之前其实是相对独立的。虽然每个组件的数据是可以独立处理,但是组件与组件之间的数据还是存在联系的,而所有的组件之间的数据关系,都是可以归结为父组件跟子组件之间的数据传递关系。再拆分这种数据传递关系,就是要解决父组件数据传给子组件子组件数据传给父组件两个问题。

  1. 父组件传入参数。在React中,父组件给子组件传递数据其实挺简单的,就是在子组件标签中通过变量进行传入,子组件通过props获取传入的数据:

代码1 父组件传参

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
/* 自己写豆瓣的一段代码,
* 图书页面Book组件中把页面类型参数Contants.Book传入
* 搜索Search组件
* /
class Book extends Component {
render () {
return (
<Fragment>
<Search
page={this.state.book}
searchInfo={this.getSearchInfo}
/>
</Fragment>
)
}
}

/* 搜索Search组件通过props.page接收父组件的参数,
* 判断input标签的属性placeholder的值
* 项目中还使用了styled-components插件,后面会另有介绍
* /
class Search extends Component {
// 根据父组件传入的props.page确定input标签的属性placeholder的值
showPlaceHolder () {
const { placeHolders, page } = this.props
const placeHoldersJS = placeHolders.toJS()
return placeHoldersJS[page]
}
render () {
return (
<SearchWrapper>
<div>
<SearchInput
value={this.props.keyword}
placeholder={this.showPlaceHolder()}
/>
</div>
</SearchWrapper>
)
}
}

上面的代码有所省略,但基本上是对父子组件之前传递数据有基本的实现。注意,父组件的数据需要在子组件标签内用一个变量传入,这个变量将会放在子组件的props属性中。子组件调用这个变量时需要到props中调用。

  1. 子组件传出参数。一般来说,React的数据流是单向流动,即父组件的数据可以流入子组件,但是子组件的数据是不能向父组件传递或修改父组件的数据。但是子组件是可以通过自己的事件处理函数,手动触发父组件传递进来的回调函数,在回调函数上把数据通过参数传递进去。这样就可以让子组件当数据有变化的时候,父组件也可以根据变化进行响应处理:

代码2 子组件调用父组件回调函数

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
// 父组件Book组件向子组件Search先传入getSearchInfo()方法
class Book extends Component {
constructor (props) {
super(props)
this.getSearchInfo = this.getSearchInfo.bind(this)
}

getSearchInfo (newKeyword) {
/* 省略代码 */
}

render () {
const { pullDownStatus, pullUpStatus } = this.props
return (
<Fragment>
<Search
searchInfo={this.getSearchInfo}
/>
</Fragment>
)
}
}

// 子组件在handleClick()方法中调用父组件传入的回调函数searchInfo(keyword),并把值传入
class Search extends Component {
constructor (props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}

handleClick () {
const { searchInfo, keyword} = this.props
if (keyword !== '') {
searchInfo(keyword)
}
}

render () {
return (
<SearchWrapper>
<div>
<SearchInput />
<SearchButton
onClick={this.handleClick}
>搜索</SearchButton>
</div>
</SearchWrapper>
)
}
}

这里有个地方需要注意的。父组件方法getSearchInfo()虽然是对象中的方法,但是它也有自己的作用域,所以正常来说它的this指向它自己的作用域,this.props就不存在了。所以需要在constructor()中指定它的作用域为整个类,需要补上this.getSearchInfo = this.getSearchInfo.bind(this)

生命周期

我们来回头说一下组件的生命周期。组件的生命周期包括了初始化,第一次加载渲染,更新渲染跟卸载四个阶段。而React的生命周期函数主要分布在后面三个阶段。我们先每个阶段简单整理一下:

初始化

这个阶段主要还是调用了ES6中类的constructor()。通过构造器完成对父类的继承,变量的初始化和函数指定作用域。这个阶段还没有调用生命周期函数。

第一次加载渲染

第一次加载渲染的过程会调用三个生命周期函数:

1.componentWillMount(),这个函数将会在组件第一次加载渲染之前被调用。可以做一些组件渲染到页面前的操作。只调用一次。

2.render(),这个函数就是负责创建虚拟DOM,此时所有数据都至少已经初始化了。

3.componentDidMount(),这个函数是在组件第一次加载渲染完成之后马上被调用的,也只调用一次。这个函数可以调用ajax请求,返回的数据会在后面阶段更新组件状态,并触发重新渲染

更新渲染

第一次加载渲染完成之后到组件被卸载之前,组件中的state状态跟props数据还是可以改变。所以会调用下面几个生命周期函数:

1.componentWillReceiveProps(nextProps),在组件第一次加载渲染之后,如果props数据将要发生改变,那改变之前会先调用这个函数。函数的传参为props改变之后的值。

2.shouldComponentUpdate(nextProps,nextState),在组件props或state将发生改变,进行更新渲染之前会调用这个函数。函数的传参为props或者state改变之后的值。函数最后返回值一定是布尔值,表示是否重新渲染组件(true为重新渲染,false为阻止渲染)。这里可以判断props或state的值是否达到需要重新渲染组件的要求。这个函数的判断有助于提高页面性能,因为当父组件重新渲染时也会导致其所有子组件重新渲染,所以在组件中做这个判断可以阻止不必要的重新渲染。

3.componentWillUpdate (nextProps,nextState),在组件调用shouldComponentUpdate(nextProps,nextState)并返回true之后,进行更新渲染之前会调用这个函数。这里同样可以拿到nextPropsnextState进行操作。

4.render(),这里的渲染就是根据新更新的propsstate的值进行重新创建虚拟DOM,然后在根据diff算法比对新旧DOM数,找到有差异的最小DOM节点,并重新渲染。

5.componentDidUpdate(prevProps,prevState),在组件重新渲染之后会调用这个函数。函数的传参为props或者state改变之前的值。

组件卸载

当组件将不再被渲染到页面上时,组件将会被卸载。卸载前只有一个函数会被触发调用:

componentWillUnmount () ,这个函数因为是在组件被卸载前被调用,所以很适合以下一些操作:

1.清除组件内的所有定时setTimeoutsetInterval

2.移除组件内的所有监听事件 removeEventListener

3.处理未完成的ajax请求。一般ajax请求之后都会修改state,导致会调用组件的setState(),如果组件被卸载的时候ajax请求未完成,会导致请求完成后调用报错,所以可以在组件内设置状态值控制ajax请求完成后是否执行组件内setState(),然后在componentWillUnmount ()中修改状态值。

父子组件的生命周期

这里拓展一下,父子组件的生命周期被调用也是有顺序的。

在组件初次渲染阶段,会从父组件开始,先调用父组件的constructor()构造函数、componentWillMount(),然后接着子组件开始调用这两个函数,直到最底层子组件调用这两个函数。接着调用对底层子组件的componentDidMount(),再一层层往上调用父组件的这个函数,直到最顶层父组件。

在组件的卸载阶段,则会先从最顶层父组件开始调用componentDidUnmount(),直到最底层子组件。

父子组件之间的性能优化

其实在React中,父组件的数据变化,是会影响子组件进行重新渲染,倒是出现不必要的性能消耗。

前面有说到,可以通过shouldComponentUpdate(nextProps,nextState)函数来判断是否需要重新渲染组件,但这样一来,每个组件都要手动判断重新渲染。每次只要出现父组件传来的数据,包括state状态的变化,都会调用这个函数,也会导致代码冗余。所以React还提供了一种处理方案。

React V15中就引入了React.PureComponent。这个PureComponentComponent用法上是一样的,只是组件继承了PureComponent类后,组件会自动实现propsstate的浅比较。当组件的propsstate的类型都比较简单的时候,就可以直接使用PureComponent。当如果propsstate是嵌套对象或数组时,浅比较将得不到预期的结果。

关于 React的内容就先把这些基础的先介绍到这里,主要还是要看文档,这里的就算是一些个人整理。下一篇将会介绍style-components插件。