Skip to main content

组件编程-组件生命周期

案例需求

  • 让指定的文本做显示 / 隐藏的渐变动画
  • 从完全可见,到彻底消失,耗时2S
  • 点击“不活了”按钮从界面中卸载组件

案例展示

需求引出生命周期

<script type="text/babel">

class Life extends React.Component{

state = {
opacity:1
}

RemoveComponent = event=>{
ReactDOM.unmountComponentAtNode(document.getElementById("test"))
}

componentWillUnmount(){
clearInterval(this.timer)
}

componentDidMount(){
let {opacity} = this.state;
this.timer = setInterval(()=>{
opacity -= 0.1;
if(opacity <= 0) opacity = 1;
this.setState({opacity})
}, 200)
}

render(){
return (
<div>
<h3 style={{opacity:this.state.opacity}}>react 学不会, 怎么办</h3>
<button onClick={this.RemoveComponent}>不活了</button>
</div>
)
}
}

ReactDOM.render(<Life/>, document.getElementById("test"))
</script>
  • componentWillUnmount(){} - 组件卸载前回调
  • componentDidMount(){} - 组件挂载后回调
  • render(){} - 组件调用

概念

  • 组件从创建到死亡它会经历一些特定的阶段
  • React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用
    • 生命周期回调函数
    • 生命周期钩子函数
    • 生命周期函数
    • 生命周期钩子
  • 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作

总结

重要的钩子

  1. render:初始化渲染或更新渲染调用
  2. componentDidMount:开启监听, 发送ajax请求
  3. componentWillUnmount:做一些收尾工作, 如: 清理定时器

17版即将废弃的钩子

  1. componentWillMount
  2. componentWillReceiveProps
  3. componentWillUpdate

现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用

老版生命周期-17版前

  • 初始化阶段: 由ReactDOM.render()触发, 初次渲染
    • constructor() - 构造器
    • componentWillMount() - 挂载前调用
    • render() - 组件挂载(渲染)
    • ==componentDidMount() - 挂载后调用==
      • 一般作用为初始化 例如: 开启定时器, 发送网络请求, 订阅消息…
  • 更新阶段: 由组件内部 this.setState()父组件render触发
    • componentWillReceiveProps() - 接收新的props
      • ==这个第二次传入props调用==
    • shoudComponentUpdate() - 判断组件是否应该更新了( 默认为真 )
    • componentWillUpdate() - 组件将要更新
    • ==render() - 组件挂载(渲染)==
    • componentDidUpdate() - 组件完成更新
  • 卸载阶段: 由 ReactDOM.unmountComponentAtNode() 触发
    • ==componentWillUnmount() - 组件将要卸载==
      • 一般作用为一些收尾工作 例如, 关闭定时器, 取消订阅消息…

案例: (挂载时)按键后计数++

<script type="text/babel">
class Count extends React.Component{
// 构造函数
constructor(props){
console.log("Conut--constructor()")
super(props)
this.state = {count:0}
}

// 组件将挂载钩子
componentWillMount(){
console.log("Count--componentWillMount()")
}

// 组件完成挂载钩子
componentDidMount(){
console.log("Count--componentDidMount()")
}

// 组件控制更新的"阀门"
shouldComponentUpdate(){
console.log("Count-shouldComponentUpdate()")
return true;
}

// 组件强制将要更新钩子
componentWillUpdate(){
console.log("Count--componentWillUpdate()")
}

// 组件更新完毕钩子
componentDidUpdate(){
console.log("Count--componentDidUpdate()")
}

// 按钮+1
add = event=>{
const {count} = this.state;
this.setState({count:count+1})
}

// 组件卸载
remove = event=>{
ReactDOM.unmountComponentAtNode(document.getElementById("test"))
}

// 组件强制刷新
force = event=>{
this.forceUpdate();
}

render(){
console.log("Count--render()")
const {count} = this.state
return (
<div>
<h3>当前计数为{count}</h3>
<button onClick={this.add}>计数++</button>
<button onClick={this.remove}>卸载组件</button>
<button onClick={this.force}>不刷新状态, 强制更新</button>
</div>
)
}
}

ReactDOM.render(<Count/>, document.getElementById("test"))
</script>

案例: (父组件)换车~

<script type="text/babel">

class A extends React.Component{
state = {carname:"奔驰"}

changeCar = ()=>{
this.setState({carname:"奥托"})
}

render(){

const {carname} = this.state

return (
<div>
<div>我是组件A</div>
<button onClick={this.changeCar}>按下按键更新换车</button>
<B carname={this.state.carname}/>
</div>
)
}
}

class B extends React.Component{
// 组件将要更新props钩子
componentWillReceiveProps(props){
console.log("B--componentWillReceiveProps()", props)
}

// 控制组件刷新的"阀门"
shouldComponentUpdate(){
console.log("B--shouldComponentUpdate()")
return true
}

// 组件将要更新
componentWillUpdate(){
console.log("B--componentwillUpdate()")
}

// 组件完成更新
componentDidUpdate(){
console.log("B--componentDidUpdate()")
}

render(){
console.log("B--render()")

return (
<div>
<h3>我的车是 {this.props.carname}</h3>
</div>
)
}
}

ReactDOM.render(<A/>, document.getElementById("test"))
</script>

新版本生命周期-17版及后

  • 初始化阶段: 由 ReactDOM.render() 触发, 初次渲染
    • constructor() - 构造器
    • getDerivedStateFromProps()
      • state的值在任何时候都取决于 props ,那么可以使用etDerivedStateFromProps()
      • 也可以直接使用构造器
    • render()
    • ==componentDidMount() - 组件完成挂载==
      • 一般作用为初始化 例如: 开启定时器, 发送网络请求, 订阅消息…
  • 更新阶段: 由组件内部 this.setState()父组件render触发
    • getDerivedStateFromProps()
      • state的值在任何时候都取决于 props ,那么可以使用etDerivedStateFromProps()
      • 也可以直接使用构造器
    • shoudComponentUpdate() - 组件判断是否应该更新
    • ==render() - 组件挂载(渲染)==
    • getSnapshotBeforeUpdate() - 更新之前获取快照
    • componentDidUpdate() - 组件完成更新
  • 卸载阶段: 由 ReactDOM.unmountComponentAtNode() 触发
    • ==componentWillUnmount() - 组件将要卸载==
      • 一般作用为一些收尾工作 例如, 关闭定时器, 取消订阅消息…

getSnapshotBeforeUpdate()-鲜有的应用场景

滚动页面定位

<style>

.list{
height: 150px;
width: 300px;
background-color: aqua;
overflow: auto;
}

.news{
height: 30px;
}

</style>
<script type="text/babel">

class NewList 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; // 获取当前列表总高
}

/* (preProps,preState,snapshotValue) */
componentDidUpdate(preProps, preState, height){
// 右值等于一个新闻高度
// 左值为已经偏移完的高度
return this.refs.list.scrollTop += this.refs.list.scrollHeight - height;
}

render(){
return(
<div className="list" ref="list">
{
/* n 表示遍历后的节点对象 */
this.state.newsArr.map((n, index)=>{
return(
<div key={index} className="news">{n}</div>
)
})
}
</div>
)
}

}

ReactDOM.render(<NewList/>, document.getElementById("test"))

</script>