react 17版本
- 使用getDerivedStateFromProps接收外部数据同步到本地state。
- componentDidUpdate里面发送异步请求。
Foo.js
import { useState } from 'react'
import GetDerivedStateFromProps164plus from './GetDerivedStateFromProps164plus'
// 父组件重新render引起子组件重新render的情况有两种
const App = () => {
const [data, setData] = useState(111)
return (
<>
<button onClick={() => setData(data)}>addData 父组件不更新data</button>
<button onClick={() => setData(data + 1)}>addData + 1 父组件更新data</button>
<GetDerivedStateFromProps164plus data={data}></GetDerivedStateFromProps164plus>
</>
)
}
export default App
Child.js
import React, { Component, useState } from 'react'
// (React v16.3中)getDerivedStateFromProps 本来是只在创建和更新(由父组件引发部分)中调用。如果不是由父组件引发,
// 那么getDerivedStateFromProps也不会被调用,如自身setState引发或者forceUpdate引发。
// (React v16.4中)改正了这一点,让getDerivedStateFromProps无论是Mounting还是Updating,
// 也无论是因为什么引起的Updating,全部都会被调用
class Child extends Component {
constructor(props) {
super(props)
this.state = {
list: [3,4,5],
data: 1,
props: '' // 绑定一个props引用,设置为空方便下次传入的时候比较,并且能在第一次传入的时候执行state.data同步。如果是直接将super的props赋值给state.props,则第一次不会同步,因为props的值相同。
}
console.log(this.state)
}
// static getDerivedStateFromProps(prevprops, nextState)
// 函数作用:这个函数主要是用来代替 componentWillReceiveProps,作用是接收props转换成本地的state。
// 参数:props是传入的props,state是更新后的state
// 返回值:在组件创建时和更新时的render方法之前调用,它应该返回一个对象来更新状态,或者返回null来不更新任何内容。
// 返回值:null或者一个新的state对象。
// 这里无法访问this。因为static变量只能被class访问,而无法被实例访问。
// 即可以Child.xxx,而不能(new Child()).xxx
static getDerivedStateFromProps(prevprops, nextState) {
console.log('getDerivedStateFromProps (1)')
console.log(prevprops.data !== nextState.props.data)
console.log(prevprops.data)
console.log(nextState.props.data)
console.log(nextState.data)
// 如果props传入值和原来的值不一样,则重新给state赋值
if(prevprops.data !== nextState.props.data) {
return {
data: prevprops.data,
props:{...prevprops}
}
}
// 默认不改变state。
return null
}
// 异步加载,参数如果更新则发送请求
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate (3)')
console.log(prevProps)
console.log(this.state.data)
console.log(prevState.data)
// 和之前的state.data对比,发现参数更新了,则执行请求。
console.log(prevState.data !== this.state.data)
if(prevState.data !== this.state.data) {
return this._doAsyncOperation(this.state.data)
};
}
// 感觉比在componentDidUpdate中更实时更新
// componentWillReceiveProps(nextProps) {
// if (this.props.data !== nextProps.data) {
// // 在这里进行异步操作或者更新状态
// this.setState({
// data: this.props.data,
// });
// this._doAsyncOperation(this.props.data);
// }
// }
// 有数据更新则通过,否则不更新组件。
shouldComponentUpdate(nextProps, nextState) {
console.log('shouldComponentUpdate(2)')
console.log('外部数据: ',nextProps.data , this.state.data)
console.log('list是否改变:', nextState.list !== this.state.list)
console.log('data是否改变:', this.state.data !== nextState.data)
console.log('本地总数据是否改变:', this.state.list !== nextState.list || this.state.data !== nextState.data )
console.log('本地数据:', this.state, nextState)
// 外部变量判断
// 外部更新数据,需要渲染
if(nextProps.data !== this.state.data) {
return true
}
// 本地变量判断
if( this.state.list !== nextState.list || this.state.data !== nextState.data ) {
return true
}
return false
}
// 请求数据
_doAsyncOperation = (params) => {
setTimeout(() => {
let data = this.state.list.slice()
data.push(params)
this.setState({
list: [...data]
})
}, 1000)
}
handleClick = () => {
this.setState({
data: this.state.data
})
}
handleClick2 = () => {
this.setState({
data: this.state.data + 1
})
}
render () {
return (
<div>
<hr></hr>
--- propsToState Render ----
<br />
{console.log('propsToState Render')}
<p>
props: {this.props.data}
</p>
<p>
state: {this.state.data}
</p>
<ul>
{
this.state.list.map((item, index) => (
<li key={index}>{item}</li>
))
}
</ul>
<button onClick={this.handleClick}>addData</button>
<button onClick={this.handleClick2}>addData + 1</button>
</div>
)
}
}
export default Child
效果:
- 父子组件的addData按钮不会触发更新
- addData+1按钮会触发更新,列表增加item
- 父组件addData+1按钮,这个看传入的值和本地的state是否相同,相同则不更新,否则更新。
请求的整个流程如下:
- 子组件点击addData+1按钮,更新state.data
- 走getDerivedStateFromProps判断,无更新
- 走组件更新shouldComponentUpdate,需要更新state.data
- 组件渲染完毕走componentDidUpdate,data发生改变,请求数据
- 异步请求后更新state。
- 从新进入循环判断,即回到2-4
- 走getDerivedStateFromProps判断,无更新
- 走组件更新shouldComponentUpdate,需要更新state.list
- 组件渲染完毕走componentDidUpdate,data无变更,不再请求数据。
如果是父组件点击addData+1按钮,则是多了走stateToProps的更新步骤,然后走到shouldComponentUpdate则看是否和本地数据一样,如果一样就被中断更新state.data,也就不会再去发送请求。
参考:React生命周期(包括react16版)