React 项目中 UI 的改变来源于 state 改变,类组件中 setState 是更新组件,渲染视图的主要方式。
基础用法:
setState(object,callback)
- 参数1: object、object作为对象,代表即将合并的state,如果作为一个函数,那当前组件的state和props将作为参数,返回值用于新的state。
- 参数2: callback、callback作为函数,函数执行上下文中可以获取当前state更新后最新的state值,可以作为依赖state变化的副作用函数,可以用来做一些基于dom的操作。
this.setState({参数1:1, 参数2:2},()=>{
// 获取最新的参数
});
this.setState((state, props)=>{
// return state
});
state触发后的底层机制:
render:阶段:
- setState会产生一个新优先级, 计算(expirationTime)。
- react 从fiber Root 节点开始向下调和子节点,并对比发生更新的地方,合并state 触发render函数,获取新的UI视图。
commit阶段:
- 替换真实dom 完成流程更新。
- 执行callback 函数。
执行顺序:
1、render阶段函数执行。 2、commit阶段替换真实dom。 3、回调执行callback。
类组件初始化过程中绑定了负责更新的Updater对象,对于如果调用 setState 方法,实际上是 React 底层调用 Updater 对象上的 enqueueSetState 方法。
enqueueSetState(inst, payload, callback) {
const fiber = ReactInstanceMap.get(inst);
const currentTime = requestCurrentTime();
const expirationTime = computeExpirationForFiber(currentTime, fiber);
// 当调用setState react 会创建一个update
const update = createUpdate(expirationTime);
update.payload = payload;
// 判断有无callback函数
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'setState');
}
update.callback = callback;
}
// 将当前的update 存入fiber中的待更新队列中
enqueueUpdate(fiber, update);
// 执行调度更新
scheduleWork(fiber, expirationTime);
},
batchedEventUpdates 批量更新:
批量更新与用户事件相关,
function batchedEventUpdates(fn,a){
/* 开启批量更新 */
isBatchingEventUpdates = true;
try {
/* 这里执行了的事件处理函数, 比如在一次点击事件中触发setState,那么它将在这个函数内执行 */
return batchedEventUpdatesImpl(fn, a, b);
}finally
{ /* try 里面 return 不会影响 finally 执行 */ /* 完成一次事件,批量更新 */
isBatchingEventUpdates = false;
} }
类组件如何限制 state 带来的更新作用:
- pureComponent 可以对 state 和 props 进行浅比较,如果没有发生变化,那么组件不更新。
- shouldComponentUpdate 生命周期可以通过判断前后 state 变化来决定组件需不需要更新,需要更新返回true,否则返回false。
函数组件的state:
基本用法:[ state , setState ] = useState(initData)
- state:数据源。
- setState: 改变state的函数。
- initData: 初始化的值。
initData两种情况:
- 非函数,仅为初始化的值。
- 函数,返回值为初始化的值。
setState的参数两种情况:
- 非函数,作为新值赋值给state,作为下次渲染使用。
- 函数,返回值作为新的 state。
const [ number , setNumber ] = useState(0)
// 非函数
const handleClick=()=>setNumber(1)
// 函数
const handleClick=()=>{
setNumber((state)=> state + 1) // state - > 0 + 1 = 1 }
函数组件使用useEffect监听state 变化!!!
把 state 作为依赖项传入 useEffect 第二个参数 deps!!!
setState 与 useState 的异同点:
相同:
setState和 useState 更新视图,底层都调用了 scheduleUpdateOnFiber 方法,而且事件驱动情况下都有批量更新规则。
不同:
1、非pureComponent组件模式下 setState 不会进行浅比较, 只要调用state 就会更新。 useState中的dispatchAction会默认比较两次的state是否相同,然后决定是否更新组件。
2、setState有专门监听state变化的callback函数,useState 只能依靠useEffect 监听state变化。
3、setState 在底层处理逻辑上主要是和老 state 进行合并处理,而 useState 更倾向于重新赋值。
参考文献: React 进阶实践指南