目录
执行过程
异步
React18与自动批处理
setState
推荐用法 ()=>{return },this.state.
生命周期
数据没改变时不渲染
shouldComponentUpdate
PureComponent自动(推荐)
你真的理解setState吗? - 掘金
组件的私有属性,值是对象,当 state 发生变化时会触发 React 组件重新渲染视图
即响应式数据
执行过程
_pendingStateQueue
:当前组件等待执行更新的state
队列isBatchingUpdates
:react用于标识当前是否处于批量更新状态,所有组件公用dirtyComponent
:当前所有处于待更新状态的组件队列
事务机制:数据库特有的术语,同步发生数据更新时,防止数据的不一致。
1.将setState传入的partialState
参数存储在当前组件实例的state暂存队列中。
2.判断当前React是否处于批量更新状态,如果是,将当前组件加入待更新的组件队列中。
3.如果未处于批量更新状态,将批量更新状态标识设置为true,用事务再次调用前一步方法,保证当前组件加入到了待更新组件队列中。
4.调用事务的waper
方法,遍历待更新组件队列依次执行更新。
5.执行生命周期componentWillReceiveProps
。
6.将组件的state暂存队列中的state
进行合并,获得最终要更新的state对象,并将队列置为空。
7.执行生命周期componentShouldUpdate
,根据返回值判断是否要继续更新。
8.执行生命周期componentWillUpdate
。
9.执行真正的更新render
。
10.执行生命周期component
DidUpdate
。
异步
setState
其实本身执行的过程和代码都是同步的
自动批处理:同一时机多次调用`setState()`方法的一种处理机制,有助于减少在状态更改时发生的重新渲染次数。
handleClick = () => {
this.setState({
msg: 'hi'
});
this.setState({
count: 1
});
}
//虽然调用了两次`setState()`方法,但是只会触发一次`render()`方法的重新执行。
React18与自动批处理
在React18之前也有批处理的,但是在Promise、setTimeout、原生事件中是不起作用的,只在合成事件和钩子函数有用
原生自带的事件监听
addEventListener
,或者也可以用原生js、jq直接document.querySelector().onclick
这种绑定事件的形式都属于原生事件。react为了解决跨平台,兼容性问题,自己封装了一套事件机制,代理了原生的事件。
像在
jsx
中常见的onClick
、onChange
这些都是合成事件
handleClick = () => {
setTimeout(()=>{
this.setState({
msg: 'hi'
});
this.setState({
count: 1
});
}, 2000)
}
//上面代码在React18之前的版本中,将会触发两次`render()`方法。
//默认是自动批处理的,当然也可以改成不是自动批处理的方式,通过`ReactDOM.flushSync`这个方法。
import { flushSync } from 'react-dom'; // Note: react-dom, not react
handleClick = () => {
ReactDOM.flushSync(()=>{
this.setState({
msg: 'hi'
});
})
ReactDOM.flushSync(()=>{
this.setState({
count: 1
});
})
}
useState(initialState: S | (() => S));
let { useState } = React;
let { flushSync } = ReactDOM;
let Welcome = (props) => {
const [count, setCount] = useState(0);
const handleClick = () => {
flushSync(()=>{
setCount(count + 1)
})
}
return (
<div>
<button onClick={handleClick}>点击</button>
<div>hello world, { count }, { msg }</div>
</div>
);
}
当所有组件didmount
后,父组件didmount
,会将isBranchUpdate
设置为false。这时将执行之前累积的setState,
更新时会把每个组件的更新合并,每个组件只会触发一次更新的生命周期。
setState
setState(state: (prevState, props) => (return newState),callback?: () => void): void;
在状态更新完成后会调用该回调函数callback。
props 同步更新,state 异步更新的,最好不要用prevState值去计算下一个 state 的值
this.setState((prevState, props) => { return { count: prevState.count + 1 }})
state:如果是一个对象,那么会将该对象与原状态进行浅合并;
this.setState({count:1})
//箭头函数:相当于直接使用返回对象 {count: 1}
this.setState(() => ({count: 1}));
//同样的数据修改只会修改一次,可利用`setState()`的回调函数写法来保证每一次都能触发。
//因为不能比较函数体是否一致
this.setState((state)=> ({count: state.count + 1}));
this.setState((state)=> ({count: state.count + 1}));
浅合并(shallow merge)是指将两个对象进行合并,如果两个对象有相同的属性名,则后一个对象的属性值会覆盖前一个对象的属性值
但是,如果属性值是一个对象,则不会进行递归合并,而是直接用后一个对象的属性值替换前一个对象的属性值。
useState不会合并之前的值
const [info, setInfo] = useState({
username: 'xiaoming',
age: 20
})
setInfo({
...info,
username: 'xiaoqiang'
})
推荐用法 ()=>{return },this.state.
setState时使用函数返回新state值,(避免多个 setState
同时调用导致的状态更新冲突问题)
在回调函数中获取最新更新后的state
handleClick = () => {
this.setState(() => { return { board: this.jsonMsg[0].data.board_data[Number(key)] };},
() => {
console.log('Updated count:', this.state.count);
});
};
生命周期
生命周期钩子函数就是回调函数
挂载
- constructor():在 React 组件挂载之前,会调用它的构造函数。(注:如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。)
- render(): class 组件中唯一必须实现的方法。
- componentDidMount():在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。
更新
- render(): class 组件中唯一必须实现的方法。一旦组件被添加到 DOM,它只有在 prop 或状态发生变化时才可能更新和重新渲染。
- componentDidUpdate():在更新后会被立即调用。首次渲染不会执行此方法。
卸载
- componentWillUnmount():在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。
可以看到挂载时和更新时都有render
这个方法。这就是为什么state改变的时候,会触发render
重渲染操作。
数据没改变时不渲染
shouldComponentUpdate
在调用`setState()`方法的时候,如果数据没有改变,实际上也会重新触发`render()`方法。
shouldComponentUpdate = (nextProps, nextState) => {
if(this.state.msg === nextState.msg){
return false;
}
else{
return true;//重新渲染
}
}
PureComponent自动(推荐)
自动完成选择性的渲染
export default class App extends PureComponent<any, any, any>{
handleClick = () => {
this.setState({
list: [...this.state.list, 'd']
});
//错误✖
/* this.state.list.push('d');
this.setState({
list: this.state.list
}) */
}
}