生命周期汇总图
生命周期的三个阶段(旧)
1. 初始化阶段: 由ReactDOM.render()触发—初次渲染
-
constructor()
-
componentWillMount()
-
render()
-
componentDidMount()
2.更新阶段: 由组件内部this.setSate()或父组件重新render触发
-
shouldComponentUpdate()
-
componentWillUpdate()
-
render()
-
componentDidUpdate()
3.卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount()
图示如下:
线路一 初次渲染阶段
class Demo extends React.Component {
// 实例化组件时执行
constructor () {
super()
this.state = {
count: 0
}
console.log('constructor')
}
handleAdd = () => {
this.setState({
count: this.state.count + 1
})
}
handleUnmounted = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
// 将要挂载时执行
componentWillMount () {
console.log('componentWillMount')
}
// 渲染时执行
render () {
console.log('render')
return (
<div>
<div>{this.state.count}</div>
<button onClick={this.handleAdd}>+1</button>
<button onClick={this.handleUnmounted}>卸载</button>
</div>
)
}
// 挂载完毕时执行
componentDidMount () {
console.log('componentDidMount')
}
// 将要卸载时执行
componentWillUnmount () {
console.log('componentWillUnmount')
}
}
ReactDOM.render(<Demo />, document.getElementById('app'))
线路二 通过修改state
class Demo extends React.Component {
constructor () {
...
}
handleAdd = () => {
...
}
handleUnmounted = () => {
...
}
// 返回 true 时,后续的回调函数才有机会执行,相当于控制组件更新的‘阀门’
shouldComponentUpdate() {
console.log('shouldComponentUpdate')
return true
}
componentWillMount () {
console.log('componentWillMount')
}
// 组件将要更新时执行
componentWillUpdate () {
console.log('componentWillUpdate')
}
render () {
...
}
// 组件更新完毕时执行
componentDidUpdate () {
console.log('componentDidUpdate')
}
componentDidMount () {
console.log('componentDidMount')
}
componentWillUnmount () {
console.log('componentWillUnmount')
}
}
ReactDOM.render(<Demo />, document.getElementById('app'))
线路三 强制更新forceUpdate()
class Demo extends React.Component {
constructor () {
...
}
handleAdd = () => {
...
}
handleUnmounted = () => {
...
}
shouldComponentUpdate() {
...
}
componentWillMount () {
console.log('componentWillMount')
}
componentWillUpdate () {
console.log('componentWillUpdate')
}
render () {
console.log('render')
return (
<div>
...
// 调用 forceUpdate 强制更新,即 无需调用 setState 也会触发更新
<button onClick={()=>this.forceUpdate()}>强制更新</button>
</div>
)
}
componentDidUpdate () {
console.log('componentDidUpdate')
}
componentDidMount () {
console.log('componentDidMount')
}
componentWillUnmount () {
console.log('componentWillUnmount')
}
}
ReactDOM.render(<Demo />, document.getElementById('app'))
线路四 父组件传给子组件的props发生变化
class Parent extends React.Component{
state = {
count: 0
}
handleAdd = () => {
this.setState({
count: this.state.count + 1
})
}
// 父组件执行 render
render () {
return (
<div>
// 给子组件传递 props
<Child count={this.state.count}/>
<button onClick={this.handleAdd}>+1</button>
</div>
)
}
}
class Child extends React.Component {
// 子组件触发 ‘将收到 props 回调’
// 需要注意的是,子组件第一次收到 props 时,不会触发该回调
// 说起来叫 componentWillReceiveNewProps 或许更为贴切
componentWillReceiveProps () {
console.log('componentWillReceiveProps')
}
shouldComponentUpdate() {
console.log('shouldComponentUpdate')
return true
}
componentWillUpdate () {
console.log('componentWillUpdate')
}
render () {
return (
<div>
{this.props.count}
</div>
)
}
componentDidUpdate () {
console.log('componentDidUpdate')
}
}
ReactDOM.render(<Parent />, document.getElementById('app'))
17版本的生命周期变动
三个重要的勾子函数
无论在新版还是旧版,这三个钩子函数都没有任何大改动;
render
:初始化渲染或更新渲染调用componentDidMount
:开启监听, 发送ajax请求componentWillUnmount
:做一些收尾工作, 如: 清理定时器
即将废用的钩子函数
- React预计废弃这三个钩子:
componentWillReceiveProps
、componentWillMount
、componentWillUpdate
- 在17版本中,这三个待废弃的钩子被重命名为:
UNSAFE_componentWillReceiveProps
、UNSAFE_componentWillMount
、UNSAFE_componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。
17版本新增的两个钩子
- 在17版本中,新增了两个钩子:
getDerivedStateFromProps
、getSnapshotBeforeUpdate
getDerivedStateFromProps(props,state)
- 该方法意为 从
props
中获取派生的state
- 该方法为静态方法(
static
关键字) - 该方法接收两个参数,参数1为
props
、参数2为state
- 该方法返回
null
,或者派生的state
,页面渲染时,该派生的state
优先级高于this.state
- 该方法横跨挂载和更新,可以在任何时候决定用于渲染视图的
state
- 适用场景:该方法用法罕见,仅当需要由
props
来完全决定state
时,可能有用
class Demo extends React.Component {
// 实例化组件时执行
constructor () {
super()
this.state = {
count: 0
}
console.log('constructor')
}
handleAdd = () => {
...
}
handleUnmounted = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
static getDerivedStateFromProps (props, state) {
console.log('getDerivedStateFromProps')
return null
}
// 渲染时执行
render () {
console.log('render')
...
}
// 挂载完毕时执行
componentDidMount () {
console.log('componentDidMount')
}
}
ReactDOM.render(<Demo />, document.getElementById('app'))
- 如果返回一个派生的
state
,那么将以派生的state
中所出现的属性为准
static getDerivedStateFromProps (props, state) {
console.log('getDerivedStateFromProps')
return {count: 100} // 仅修改此处
}
getSnapshotBeforeUpdate()
该方法意为在更新之前获取快照
该方法接收两个参数,参数1 prevProps
:之前的props
;参数2 prevState
:之前的state
该方法的返回值,会传递给componentDidMount
第三个参数
componentDidMount
的三个参数:(prevProps
, prevState
, returnFromSnapshot
)
通过该钩子,可以捕获一些更新前的数据,并传递给componentDidMount
同样是用法罕见的钩子
getSnapshotBeforeUpdate使用场景小demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>4_getSnapShotBeforeUpdate的使用场景</title>
<style>
.list{
width: 200px;
height: 150px;
background-color: skyblue;
overflow: auto;
}
.news{
height: 30px;
}
</style>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>
<script type="text/babel">
class NewsList extends React.Component{
state = {newsArr:[]}
componentDidMount(){
setInterval(() => {
//获取原状态
const {newsArr} = this.state
//模拟一条新闻
const news = '新闻'+ (newsArr.length+1)
//更新状态
this.setState({newsArr:[news,...newsArr]})
}, 1000);
}
getSnapshotBeforeUpdate(){
return this.refs.list.scrollHeight //此属性是更新前的内容总高度,传给componentDidUpdate的height参数
}
componentDidUpdate(preProps,preState,height){
// ↓ 这个是新的内容总高度
this.refs.list.scrollTop += this.refs.list.scrollHeight - height
}
render(){
return(
<div className="list" ref="list">
{
this.state.newsArr.map((n,index)=>{
return <div key={index} className="news">{n}</div>
})
}
</div>
)
}
}
ReactDOM.render(<NewsList/>,document.getElementById('test'))
</script>
</body>
</html>