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> ) } }
钩子函数 | 触发时机 | 作用 |
---|---|---|
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