生命周期
初始化阶段
render
只能访问this.props和this.state,不允许修改状态和DOM输出
componentWillMount
render之前最后一次修改状态的机会
在ssr中 这个方法将会被多次调用,所以会重复触发多少遍,同时在这里如果绑定事件,将无法解绑,导致内存泄漏,变得不够安全高效逐步废弃。
因此需要加上UNSAFE
UNSAFE_componentWillMount () {
}
componentDidMount
成功render并渲染完成真实DOM之后触发,可以修改DOM
componentWillMount () {
}
运行中阶段
componentWillReceiveProps
外部组件多次频繁更新传入多次不同的props,会导致不必要的异步请求
触发
UNSAFE_componentWillReceiveProps(nextProps)
在组件接收到新的参数时被触发,当父组件导致子组件更新的时候, 即使接收的 props 并没有变化, 这个函数也会被调用.
工作方式
参数是组件接收到的新的 props , 用于比对新的 props 和原有的 props, 用户需要在函数体中调用 setState() 来更新组件的数据.
UNSAFE_componentWillReceiveProps(nextProps){
if (this.props.currentExercise.id !== nextProps.currentExercise.id){
this.setState({...nextProps.currentExercise})
}
}
shouldComponentUpdate
setState()函数在任何情况下都会导致组件重渲染吗?如果setState()中参数还是原来没有发生任何变化的state呢?
如果组件的state没有变化,并且从父组件接受的props也没有变化,那它就一定不会重渲染吗?
如果1,2两种情况下都会导致重渲染,我们该如何避免这种冗余的操作,从而优化性能?
没有导致state的值发生变化的setState是否会导致重渲染 ——【会!】
shouldComponentUpdate是一个性能调优函数,是重渲染时render()函数调用前被调用的函数,它接受两个参数:nextProps和nextState,分别表示下一个props和下一个state的值。并且,当函数返回false时候,阻止接下来的render()函数的调用,阻止组件重渲染,而返回true时,组件照常重渲染。
nextProps:修改后的属性
nextState:修改后的状态
可以根据状态和参数中的状态的对比,返回true/false来判断是否需要更新
shouldComponentUpdate (nextProps,nextState) {
return !(this.state.myname === nextState.myname) ||!(this.state.myage === nextState.myage)
}
componentWillUpdate
更新前记录DOM 状态, 可能会做一些处理,与componentDidUpdate相隔时间如果过长,会导致状态不太信
import React, { Component } from 'react'
export default class App extends Component {
state= {
myname:'retr0'
}
UNSAFE_componentWillUpdate () {
console.log('componentWillUpdate');
}
render() {
return (
<div>
{this.state.myname}
<button onClick={()=>{
this.setState({
myname:'xiaoming'
})
}}>Click</button>
</div>
)
}
}
componentDidUpdate
import React, { Component } from 'react'
export default class App extends Component {
state= {
myname:'retr0'
}
componentDidUpdate () {
console.log('componentDidUpdate');
}
render() {
return (
<div>
{this.state.myname}
<button onClick={()=>{
this.setState({
myname:'xiaoming'
})
}}>Click</button>
</div>
)
}
}
销毁阶段
componentWillUnMount
用于清除计时器,window.scroll=null等操作
import React, { Component } from 'react'
class Navar extends Component {
render () {
return (
<div style={{background:'#f99'}}>
navbar-<button onClick={this.handleClick}>Click</button>
</div>
)
}
handleClick = ()=>{
this.props.onEvent();
}
}
class Slidebar extends Component {
render () {
return (
<div style={{background:'#99f'}}>
slidebar
<ul>
<li>11111</li>
<li>22222</li>
<li>33333</li>
</ul>
</div>
)
}
//销毁声明周期
componentWillUnmount () {
console.log('componentWillUnmount');
}
}
export default class 子传父 extends Component {
state = {
isShow:true
}
render() {
return (
<div>
App
<Navar onEvent={()=>{
this.setState({
isShow : !this.state.isShow
})
}}/>
{this.state.isShow?<Slidebar/>:''}
</div>
)
}
}
新增
static getDerivedStateFromProps
getDerivedStateFromProps
是一个静态方法, 是一个和组件自身”不相关”的角色. 在这个静态方法中, 除了两个默认的位置参数 nextProps 和 currentState 以外, 你无法访问任何组件上的数据。第一次的初始化组件以及后续的更新过程中(包括自身状态更新以及父传子) ,返回一个对象作为新的state,返回null则说明不需要在这里更新state
会被频繁地触发
无论是组件调用了 setState(), 接收的 props 发生了变化, 或是父组件的更新都会导致子组件上的 getDerivedStateFromProps
被触发.
使用的时候必须非常小心
由于 getDerivedStateFromProps
会在 setState() 后被调用, 并且它的返回值会被用于更新数据. 这意味着你会在 setState() 之后触发 getDerivedStateFromProps
, 然后可能意外地再次 “setState()”.
getDerivedStateFromProps(nextProps)
函数中的第一个位置参数未必是 “新” 的 props. 在组件内调用了 setState() 时, getDerivedStateFromProps
会被调用. 但是此时的组件其实并没有获得 “新” 的 props, 是的, 这个 nextProps 的值和原来的 props 是一样的.
这就导致了我们在使用 getDerivedStateFromProps
时, 必须添加很多逻辑判断语句来处理 props 上的更新和 state 上的更新, 避免意外地返回了一个 Updater 再次更新数据, 导致数据异常.
import React, { Component } from 'react'
class Child extends Component {
state={
ajax:''
}
// UNSAFE_componentWillReceiveProps () {
// console.log('componentWillReceiveProps');
// }
componentDidUpdate () {
console.log(this.state.ajax);
}
static getDerivedStateFromProps (nextProps,currentState){
return {
ajax:nextProps.ajax
}
}
render () {
return (
<div>
child - {this.props.ajax}
</div>
)
}
}
export default class App extends Component {
state= {
ajax:9000
}
render() {
return (
<div>
<Child ajax={this.state.ajax}/>
<button onClick={()=>{
this.setState({
ajax:1000
})
}}>Click</button>
</div>
)
}
}
更优雅的做法
React 官方博客中提供了以下几种方案:
- 让表单控件变成完全受控组件, 不论是 onChange 处理函数还是 value 都由父组件控制, 这样用户无需再考虑这个组件 props 的变化和 state 的更新.
function EmailInput(props) {
return <input onChange={props.onChange} value={props.email} />;
}
- 让表单控件变成完全不受控组件, 但是具有 key 属性.
仍然用自身的数据来控制 value. 但是接收 props 中的某个字段作为 key 属性的值, 以此响应 props 的更新: 当 key 的值变化时 React 会替换 (reset)组件, 从而重新生成初始化数据.
When a key changes, React will create a new component instance rather than update the current one.
示例代码:
//组件内的代码
class EmailInput extends Component {
state = { email: this.props.defaultEmail };
handleChange = event => {
this.setState({ email: event.target.value });
};
render() {
return <input onChange={this.handleChange} value={this.state.email} />;
}
}
// 在父组件中接收 props 中的数据作为 key
<EmailInput
defaultEmail={this.props.user.email}
key={this.props.user.id}
/>
- 其他方法请参考 这里
getSnapshotBeforeUpdate
取代了 componetWillUpdate ,触发时间为update发生的时候,在render之后,dom渲染之前
返回一个值,作为componentDidUpdate的第三个参数。
import React, { Component } from 'react'
export default class App extends Component {
state = {
mytext :'retr0',
}
// componentWillUpdate () {
// console.log(this.state.mytext);
// }
getSnapshotBeforeUpdate () {
console.log(this.state.mytext,'获取滚动条的位置');
return {
y:100
}
}
componentDidUpdate(prevProps, prevState,data) {
console.log(prevProps,prevState,data);
}
render() {
console.log('render');
return (
<div>
{this.state.mytext}
<button onClick={()=>{
this.setState({
mytext:'xiaoming'
})
}}>Click</button>
</div>
)
}
}