这是我参与8月更文挑战的第九天,活动详情查看:8月更文挑战
class App extends React.Component {
constructor(props){
super(props)
console.warn("生命周期钩子函数:constructor")
}
componentDidMount(){
console.warn("生命周期钩子函数:componentDidMount")
}
render(){
console.warn("生命周期钩子函数:render")
return(
<div>
<h1>sss</h1>
</div>
)
}
}
// eslint-disable-line no-unused-vars
ReactDOM.render(<App />, document.getElementById('root'))
钩子函数 | 触发时机 | 作用 |
---|---|---|
constructor | 创建组件时最先执行 | 1.初始化state 2.为事件处理程序绑定this |
render | 每次渲染都会触发 | 渲染UI(不能调用setState() ) |
componentDidMount | 组件挂载(完成DOM渲染)后 | 1.发送网络请求2.DOM操作 |
注意: setState()既能更新状态又能更新UI。如果在render()里继续调用setState(),setState()又会调用render(),所以产生了递归。会导致报错。
class App extends React.Component {
constructor(props){
super(props)
console.warn("生命周期钩子函数:constructor")
// 初始化state
this.state={
count:0
}
}
componentDidMount(){
console.warn("生命周期钩子函数:componentDidMount")
const title = document.getElementById("title");
console.log(title)
}
render(){
console.warn("生命周期钩子函数:render")
return(
<div>
<h1>sss</h1>
<h2 id="title">中国队必胜</h2>
</div>
)
}
}
graph LR
A( render) --> C(componentDidUpdate)
钩子函数 | 触发时机 | 作用 |
---|---|---|
render | 每次组件渲染都会触发 | 渲染UI(与挂载阶段时同一个render()) |
componentDidUpdate | 组件更新(完成DOM渲染)后 | 1 发送网络请求2.DOM操作注意:如果要setState()必须放在if条件中 |
我们用点击按钮统计次数的小实验来看效果 Counter 是一个子组件,需要props进行传值
class App extends React.Component {
constructor(props){
super(props)
console.warn("生命周期钩子函数:constructor")
// 初始化state
this.state={
count:0
}
}
handleClick = ()=>{
this.setState({
count : this.state.count + 1
})
}
render(){
console.warn("生命周期钩子函数:render")
return(
<div>
<Counter count={this.state.count}/>
<button onClick={this.handleClick}>点击</button>
</div>
)
}
}
class Counter extends React.Component{
render(){
return <h1>统计次数:{this.props.count}</h1>
}
}
// eslint-disable-line no-unused-vars
ReactDOM.render(<App />, document.getElementById('root'))
点击按钮多次,可以发现 render方法被调用了
我们上面代码的 <Counter/>
组件,就是 props 更新促使重新渲染(调用render() ) 我们在 <Counter/>
组件中的render方法中打印。我们会发现每次点击按钮 都会打印一次。
class Counter extends React.Component{
render(){
console.log("Counter render")
return <h1>统计次数:{this.props.count}</h1>
}
}
forceUpdate() 强制更新。 我们把刚才的 handleClick方法中的setState() 换为 forceUpdate
handleClick = ()=>{
this.forceUpdate()
}
虽然页面的计数没有发生变化,但是render()执行了
我们在子组件<Counter/>
的componentDidUpdate中打印"Counter componentDidUpdate"
class Counter extends React.Component{
render(){
console.log("Counter render")
return <h1>统计次数:{this.props.count}</h1>
}
componentDidUpdate(){
console.log("Counter componentDidUpdate")
}
}
如图可以看出 在执行完render()后 再执行的 componentDidUpdate()
class App extends React.Component {
constructor(props){
super(props)
console.warn("生命周期钩子函数:constructor")
// 初始化state
this.state={
count:0
}
}
handleClick = ()=>{
this.setState({
count:this.state.count+1
})
}
render(){
console.warn("生命周期钩子函数:render")
return(
<div>
<Counter count={this.state.count}/>
<button onClick={this.handleClick}>点击</button>
</div>
)
}
}
class Counter extends React.Component{
render(){
console.log("Counter render")
return <h1 id="title">统计次数:{this.props.count}</h1>
}
componentDidUpdate(){
console.log("Counter componentDidUpdate")
const title = document.getElementById('title')
console.log(title,"update")
}
}
ReactDOM.render(<App />, document.getElementById('root'))
可以发现获取dom中的 次数也在增长。
直接将 setState()写到 componentDidUpdate()中,则会报错
class Counter extends React.Component{
render(){
console.log("Counter render")
return <h1 id="title">统计次数:{this.props.count}</h1>
}
componentDidUpdate(){
this.setState({})
}
}
导致了递归更新:
这个递归的过程很绕,大家可以慢慢理解一下:
正确做法如下:比较更新前后的props是否相同,来重新渲染 上一次的props通过传参数获得,本次props通过this获得。
class Counter extends React.Component{
render(){
console.log("Counter render")
return <h1 id="title">统计次数:{this.props.count}</h1>
}
componentDidUpdate(prevProps){
if(prevProps.count!==this.props.count){
this.setState({})
// 发送ajax请求的代码
}
}
}
钩子函数 | 触发时机 | 作用 |
---|---|---|
componentWillUnmount | 组件卸载(从页面消失) | 执行清理工作(比如:清理定时器) |
点击三次之后Counter组件就不会在页面中显示了,所以就会触发omponentWillUnmount|钩子函数。
lass App extends React.Component {
constructor(props){
super(props)
console.warn("生命周期钩子函数:constructor")
// 初始化state
this.state={
count:0
}
}
handleClick = ()=>{
this.setState({
count:this.state.count+1
})
}
render(){
console.warn("生命周期钩子函数:render")
return(
<div>
{
this.state.count>3? (<p>stop</p>)
: (<Counter count={this.state.count}/>)
}
<button onClick={this.handleClick}>点击</button>
</div>
)
}
}
class Counter extends React.Component{
render(){
console.log("Counter render")
return <h1 id="title">统计次数:{this.props.count}</h1>
}
componentDidUpdate(){
console.log("Counter componentDidUpdate")
const title = document.getElementById('title')
console.log(title,"update")
}
componentWillUnmount(){
console.log("卸载钩子函数:componentWillMount")
}
}
ReactDOM.render(<App />, document.getElementById('root'))
class Counter extends React.Component{
render(){
console.log("Counter render")
return <h1 id="title">统计次数:{this.props.count}</h1>
}
componentDidUpdate(){
console.log("Counter componentDidUpdate")
const title = document.getElementById('title')
console.log(title,"update")
}
componentDidMount(){
setInterval(()=>{
console.log("定时器执行中~")
},500)
}
componentWillUnmount(){
console.log("卸载钩子函数:componentWillMount")
}
}
点击三次后组件消失了 但是定时器还在执行
所以需要我们在componentWillUnmount()中清理定时器 clearInterval()需要一个id,我们将定时器的id交给this。
class Counter extends React.Component{
render(){
console.log("Counter render")
return <h1 id="title">统计次数:{this.props.count}</h1>
}
componentDidUpdate(){
console.log("Counter componentDidUpdate")
const title = document.getElementById('title')
console.log(title,"update")
}
componentDidMount(){
this.timerId = setInterval(()=>{
console.log("定时器执行中~")
},500)
}
componentWillUnmount(){
console.log("卸载钩子函数:componentWillMount")
clearInterval(this.timerId)
}
}
可以看到定时器被清理了。
注意⚠️:清理是为了防止内存泄漏问题的发生。
常用钩子函数 constructor render componentDidMount componentDidUpdate componentWillUnmount