开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
-钩子函数
钩子函数,也称生命周期函数,是描述一个组件从创建到销毁的全部过程。基于钩子函数,在组件执行的过程中(创建前|创建后|二次渲染前|组件销毁...)可以加入我们自己的逻辑和操作
【组件渲染的基本流程触发的钩子】
-
constructor创建组件(构造函数) -
componentWillMount组件第一次渲染前在这个钩子函数中的this.setState会变成同步操作
-
render第一次渲染(返回组件) -
componentDidMount组件第一次渲染后一般在这个钩子函数中进行ajax数据请求, 页面数据绑定这些操作
【组件状态数组发生改变setState|作为子组件其属性改变时(子组件的属性是父组件状态)】
-
shouldComponentUpdate是否允许组件重新渲染(返回True允许执行再次渲染操作)可以在这个钩子函数中进行判断, 是否允许一个组件进行二次|多次渲染, 进行性能优化
-
componentWillUpdate组件重新渲染前 -
render第二次及后续所有渲染触发 -
componentDidupdate组件重新渲染后
【父组件把传递给子组件的属性发生改变后触发】
-
componentWillReceiveProps作为子组件的时候才有, 子组件的属性是父组件状态, 父组件状态发生改变, 子组件属性也对应改变, 重新渲染组件触发该钩子
【组件被卸载前】
-
componentWillUnmount组件卸载前触发卸载:原内容在页面中不会消失,只是取消了其数据驱动特性,变成静态组件(死内容)
-代码实践
把每一个钩子函数都过一遍,光说不练假把式,一定要自己实践一遍
1.钩子触发流程
class LifeFunction extends React.Component {
static defaultProps = {} // 第一步赋值默认值
constructor(props, context) {
super(props, context)
console.log('Constructor() \t\t 1. 创建组件(构造函数)')
this.state = {
time: new Date().toLocaleString(),
n: 1
}
}
render() {
console.log('render() \t\t 组件渲染')
let { time, n } = this.state
return <section>
<p onClick={this.change}>时间: {time}</p>
<p>n的值: {n}</p>
</section>
}
UNSAFE_componentWillMount(...args) {
/**
* this.setState({
* n: 2
* })
* 在componentWillMount, 如果直接使用this.setState进行状态修改,
* 1. 不会触发componentUpdate
* 2. 这里的执行顺序很特殊, this.setState会在该钩子函数中同步执行
* 3. 也就是说会先进行状态的修改, 后进行第一次渲染
*/
// this.setState({
// n: 2
// })
console.log('componentWillMount() 2.组件第一次渲染前')
}
componentDidMount() {
console.log('componentDidMount() 3. 组件第一次渲染后')
}
shouldComponentUpdate(nextProps, nextStates) {
/**
* 接受两个参数:
* nextStates: 新的状态值
* nextProps: 新的属性值
* 在这个构造函数中, 如果用this.state获取状态值, 得到的是还没有被修改的状态值
* 例如:有个this.state中有个状态值n为1, 在第一次渲染之后过2秒状态被修改成2, 触状态修改->React再次渲染
* 就会触发该钩子函数, 在这个钩子函数中获取的状态值为旧的状态值, 等到真正第二次渲染之后this.state才会变成新的
*/
// nextProps | nextStates 最新修改的属性|状态
console.log('n', this.state.n)
console.log(nextStates) // 要改变的状态
console.log('shouldComponentUpdate4() 4. 组件状态改变触发,返回True允许重新渲染')
return true
}
UNSAFE_componentWillUpdate(nextProps, nextStates) {
/**
* 接受两个参数:
* nextStates: 新的状态值
* nextProps: 新的属性值
* 在这个构造函数中, 如果用this.state获取状态值, 得到的是还没有被修改的状态值
* 例如:有个this.state中有个状态值n为1, 在第一次渲染之后过2秒状态被修改成2, 触状态修改->React再次渲染
* 就会触发该钩子函数, 在这个钩子函数中获取的状态值为旧的状态值, 等到真正第二次渲染之后this.state才会变成新的
*/
console.log('n', this.state.n)
console.log(nextStates) // 要改变的状态
console.log('componentWillUpdate() 5. 组件重新渲染前')
}
componentDidUpdate(prevProps, prevStates) {
/**
* 接收两个值:
* prevProps : 被更新(旧的)的属性
* prevStates : 被更新的(旧的)状态
*/
console.log('n', this.state.n)
console.log(prevStates) // 被改变的状态
console.log('componentDidUpdate() \t 6. 组件重新渲染后')
}
UNSAFE_componentWillReceiveProps() {
console.log('我被执行了')
}
change = (e) => {
this.setState({
time: new Date().toLocaleString(),
n: 2
})
}
componentWillUnmount() {
console.log('componentWillUnmount() 组件即将被卸载')
}
}
2.子组件属性改变
组件的属性是只读的, 这里改变子组件的属性是指父组件嵌套子组件时,父组件把其状态作为属性传入子组件中,当父组件的状态改变时,会触发父组件的二次渲染,因为父组件的状态是子组件的属性,所以子组件的属性也会改变并重新渲染子组件,并触发子组件的 componentWillReceiveProps
class A extends React.Component {
constructor() {
super()
}
UNSAFE_componentWillUpdate(nextProps, nextStates) {
console.log('属性被修改触发')
console.log(nextProps)
}
render() {
return <div>
<p>{this.props.value}</p>
</div>
}
shouldComponentUpdate() {
console.log('是否进行更新')
return true
}
UNSAFE_componentWillUpdate(nextProps, nextStates) {
console.log('子组件重新渲染前')
console.log('新的值:', nextProps)
}
componentDidUpdate(prevProps, prevStates) {
console.log('子组件重新渲染后')
console.log('旧的值:', prevProps)
}
UNSAFE_componentWillReceiveProps(nextProps, nextStates) {
// 子组件的属性是父组件的状态, 父组件状态发生改变, 子组件重新渲染, 触发该函数
console.log('子组件属性被修改')
console.log('新的值:', nextProps)
}
}
class B extends React.Component {
constructor() {
super()
this.state = {
value: 'B的值'
}
}
render() {
// 复合组件, 组件嵌套(套娃)
return <div>
{/* 把父组件的状态信息作为属性传递给子组件 */}
<A value={this.state.value}></A>
</div>
}
componentDidMount() {
setTimeout(() => {
this.setState({
value: '修改了值'
})
}, 2000)
}
}
ReactDOM.render(
<section>
<B></B>
</section>,
root
)
-官方流程图
-重命名问题
componentWillMountcomponentWillReceivePropscomponentWillUpdate
这三个生命周期函数倾向于去鼓励编写不安全的代码, 这几个异步钩子函数会造成一些潜在的BUG,所以官方让我们用这三个钩子的时候加上 UNSAFE_ 前缀, 比如 UNSAFE_componentWillMount, 如果没有也没关系, 控制台会抛出一个警告, 官方也推荐让我们把抓取数据的代码转移到 componentDid 开头的函数里