目录
2.1.2getDerivedStateFromProps()
2.2.1getDerivedStateFromProps()
2.2.3getSnapshotBeforeUpdate(prevProps, prevState)组件刷新前调用
2.2.3componentDidUpdate(prevProps, prevState, snapshot)更新后
1.React 组件(Component)生命周期图解
通过以上React组件的生命周期图解可以发现,组件主要包含三个阶段(实例化阶段、状态或者属性更新显示阶段,卸载阶段),我们经常接触的是实例化阶段和状态或者属性更新显示阶段,实例化阶段主要完成组件的初始化、参数的初始化设置操作,状态或者属性更新显示阶段主要在组件(Component)运行过程中根据属性或者状态变化等更新界面显示的过程,我们需要根据这几个声明周期函数来完成页面显示和逻辑处理的任务;
2.组件(Component)生命周期说明
2.1实例化阶段函数分析
当一个组件被初始化创建是会按照如下方法调用:
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
注意:
这些方法将被认为是遗留的,您应该在新代码中避免使用它们:
UNSAFE_componentWillMount()
2.1.1constructor()
constructor构造器方法主要用于完成初始化state或者绑定方法,如果不需要做这些事情我们就不需要实现构造方法;当我们需要为React.Component子类实现constructor方法时,我们一定要在完成其他设置以前先调用super(props)方法,否则this.props没有在constructor中定义将会导致bug;
典型的,在React中constructors方法被使用仅仅有两个目的:
a.初始化state及调用示例
constructor(props){
super(props);
//设置状态值
this.state = {date: new Date()};
}
render(){
return (
<View>
<Text>Hello, world!</Text>
//读取状态值
<Text>It is {this.state.date.toLocaleTimeString()}.</Text>
</View>
);
}
componentDidMount(){
console.log("componentDidMount");
//更新状态值
this.setState({
date: new Date()
});
}
注意:setState()方法不能在构造函数中调用,在constructor方法中需要使用this.state={}初始化状态值;初始化状态值this.state={}只能在constructor方法中调用,更新状态值setState()方法在其他方法中调用;一旦调用了this.setState方法,组件一定会调用render方法,对组件进行再次渲染,不过,React框架会根据DOM的状态自动判断是否需要真正渲染
b.给Component实例绑定方法
constructor(props) {
super(props);
this.state = {isToggleOn: true};
//事件绑定
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<TouchableOpacity onPress={this.handleClick}>
<Text>{this.state.isToggleOn ? 'ON' : 'OFF'}</Text>
</TouchableOpacity>
);
}
注意:
避免复制props给state,这是一个常见的错误:
constructor(props) {
super(props);
// Don't do this!
this.state = { color: props.color };
}
这个问题是它既不必要(相反你可以直接使用this.props.color),并创建错误(更新color不会反映在状态中)。
如果您有意忽略prop更新,请仅使用此模式。 在这种情况下,将prop重命名为initialColor或defaultColor是有意义的。 然后,您可以通过在必要时 changing its key
来强制组件“reset”其内部状态。
阅读我们 blog post on avoiding derived state ,了解如果您认为需要一些state依赖props该怎么做。
2.1.2getDerivedStateFromProps()
无论是在创建初始化或者属性状态更新时,getDerivedStateFromProps方法将在调用render方法之前调用。 它应该返回一个更新状态(state)的对象,或者返回null以不更新任何状态。
此方法适仅用于其state依赖于prop变化时。 例如,实现一个<Transition>组件可能很方便,该组件比较其上一个和下一个子组件,以决定它们中的哪个进行动画制作。
导出派生状态会导致冗长的代码并使您的组件难于阅读。Make sure you’re familiar with simpler alternatives:
a.如果需要执行操作(例如,数据获取或动画)来响应props的变化,则使用componentDidUpdate
生命周期方法;
b.如果仅当props变化时,你想要重新计算一些数据, use a memoization helper instead;
c.当props变化时你想重新设置state,则考虑制作一个组件 fully controlled 或者 fully uncontrolled with a key
;
此方法不能访问组件实例。如果愿意,通过提取组件的prop和state的纯函数在类之外定义,在getDerivedStateFromProps()和其他类方法之间重用一些代码。
2.1.3componentDidMount()
componentDidMount()在组件装载完成以后立即调用(插入到树中)。需要DOM节点的初始化应该放在这里。如果需要从远程端点加载数据,那么这里是实例化网络请求的好地方。
*** 因为UI已经成功渲染,而且这里面是异步的,所以放在这个函数进行数据的请求等复杂的操作,不会出现UI错误***
2.2props或者state更新阶段函数分析
props或者state更新时,组件会重新渲染按照如下方法顺序执行:
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
注意:
这些方法将被认为是遗留的,您应该在新代码中避免使用它们:
UNSAFE_componentWillUpdate()
UNSAFE_componentWillReceiveProps()
2.2.1getDerivedStateFromProps()
2.2.2shouldComponentUpdate()
当接收到新的prop或state时,在调用render方法之前调用shouldComponentUpdate()。默认返回true。对于初始化render或使用forceUpdate()时,不会调用此方法。
这个方法仅作为性能优化而存在。不要依赖它“阻止”rendering(渲染),因为这会导致bug。考虑使用内置的PureComponent,而不是手工编写shouldComponentUpdate()。PureComponent对props和state执行简单的比较,并减少跳过必要更新的机会。
目前,如果shouldComponentUpdate()返回false,则不会调用UNSAFE_componentWillUpdate()、render()和componentDidUpdate()。将来React可能将shouldComponentUpdate()视为一个提示而不是一个严格的指令,并且返回false仍然可能导致组件的重新呈现。
2.2.3getSnapshotBeforeUpdate(prevProps, prevState)组件刷新前调用
getSnapshotBeforeUpdate()在最近呈现的渲染输出被提交给DOM以前调用。它使组件能够在可能更改之前从DOM(例如,滚动位置)捕获一些信息。此生命周期返回的任何值都将作为参数传递componentDidUpdate()。
这个用例并不常见,但是它可能出现在UI中,比如聊天线程需要以特殊方式处理滚动位置。
应返回快照(snapshot)值(或null)。
class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// Are we adding new items to the list?
// Capture the scroll position so we can adjust scroll later.
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// If we have a snapshot value, we've just added new items.
// Adjust scroll so these new items don't push the old ones out of view.
// (snapshot here is the value returned from getSnapshotBeforeUpdate)
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.listRef}>{/* ...contents... */}</div>
);
}
}
在上面的示例中,读取getSnapshotBeforeUpdate中的scrollHeight属性非常重要,因为可能在“render”阶段生命周期(比如render)和“commit”阶段生命周期(比如getSnapshotBeforeUpdate和componentDidUpdate)之间有延迟。
2.2.3componentDidUpdate(prevProps, prevState, snapshot)更新后
componentDidUpdate在更新发生后立即调用。对于初始化render不会调用此方法。
当组件被更新时,利用这个机会对DOM进行操作。这也是进行网络请求的好地方,只要您将当前props与以前的props进行比较(例如,如果props没有改变,可能不需要网络请求)。
componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
如果您的组件实现了getSnapshotBeforeUpdate()生命周期(这是非常罕见的),那么它返回的值将作为第三个“snapshot”参数传递给componentDidUpdate()。否则,这个参数将不被定义。
2.3卸载阶段函数分析
2.3.1componentWillUnmount()
componentWillUnmount()在卸载和销毁组件之前立即调用。在此方法中执行任何必要的清理,例如使计时器无效、取消网络请求或清理
componentDidMount()中创建的任何订阅。
您不应该在componentWillUnmount()中调用setState(),因为组件永远不会重新呈现。一旦卸载了组件实例,就永远不会再次安装它。
3.常用知识点分析:
this.state:开发中,组件肯定要与用户进行交互,React的一大创新就是将组件看成了一个状态机,一开始有一个初始状态,然后用户交互,导致状态变化,从而触发重新渲染UI
1、当用户点击组件,导致状态变化,this.setSate方法就修改状态值,每次修改以后,会自动调用this.render方法,再次渲染组件
2、可以把组件看成一个状态机,根据不同的status有不同的UI展示,只要使用setState改变状态值,根据diff算法算出有差值后,就会执行ReactDom的render方法,重新渲染界面
3、由于this.props和this.state都用于描述组件的特性,可能会产生混淆,一个简单的区分方法就是---this.props表示那些一旦定义,就不再更改的特性,而this.state是会随着用户交互而产生改变的特性
获取真实的Dom节点:
1、在React Native中,组件并不是真实的DOM节点,而是存在于内存中的一种数据结构,叫虚拟DOM
2、只有当它插入文档后,才会变成真实的DOM
3、根据React的设计,所有DOM变动,都现在虚拟DOM上发生,然后再将实际发生变动的部分,反映在真实DOM上,这种算法叫做DOM diff,它可以极大提高网页的性能表现。
4、有时需要从组建获取真实DOM节点,这时就需要到ref属性
4.什么是DOM diff算法
Web界面由DOM树来构成,当其中某一部分发生变化时,其实就是对应的某个DOM节点发生了变化,在React中,构建UI界面的思路是由当前状态决定界面,前后两个状态就对应两套界面,然后由React来比较两个界面的区别,这就需要对DOM树进行Diff算法分析。
相关代码:
4.1.index.js
import React, {Component} from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity
} from 'react-native';
import {Actions} from 'react-native-router-flux';
import HomeDetails from './HomeDetails';
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
clickText: "开始点击按钮",
count: 1,
detailContent: true
}
}
componentWillMount() {
console.log("componentWillMount1111");
}
shouldComponentUpdate(nextProps, nextState){
console.log(this.state.detailContent,'detailContent');
if (this.state.count !== nextState.count) {
console.log("shouldComponentUpdate1111---组件需要更新");
return true;
}
return false;
}
componentWillUpdate(){
console.log("componentWillUpdate1111---组件将要更新");
}
componentDidUpdate(){
console.log("componentDidUpdate1111---组件更新完毕");
}
componentDidMount() {
console.log("componentDidMount1111");
}
componentWillUnmount() {
console.log("componentWillUnmount1111");
}
clickButton(){
const { count } = this.state;
this.setState({
clickText: "我点击了按钮",
count: count + 1,
detailContent: false
})
}
render() {
console.log("render1111");
return (
<View style={styles.container}>
<Text>欢迎来到首页</Text>
<TouchableOpacity
onPress={() => Actions.notice()}
>
<Text>跳转到公告页</Text>
</TouchableOpacity>
<Text style={{color: 'blue', fontSize: 40}}>{this.state.count}</Text>
<TouchableOpacity
style={styles.button}
onPress={() => this.clickButton()}
>
<Text>{this.state.clickText}</Text>
</TouchableOpacity>
<HomeDetails detailContent={this.state.detailContent}/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center"
},
button: {
width: 250,
height: 60,
backgroundColor: 'red',
borderRadius: 10,
alignItems: 'center',
justifyContent: 'center'
}
});
4.2子组件HomeDtails.js
import React, {Component} from 'react';
import {
View,
Text,
StyleSheet
} from 'react-native';
export default class HomeDetails extends Component {
constructor(props) {
super(props);
this.state = {}
}
componentWillMount() {
}
componentWillReceiveProps(nextProps){
console.log(this.props.detailContent,'this--->>componentWillReceiveProps');
console.log(nextProps.detailContent,'next--->>componentWillReceiveProps')
}
componentDidMount() {
}
componentWillUnmount() {
}
render() {
return (
<View style={styles.container}>
<Text>欢迎HomeDetails</Text>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center"
}
});
参考:
React Component最新的生命周期图解说明
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
React.Component
https://reactjs.org/docs/react-component.html#componentdidupdate
react-native 生命周期