useState的实现使用了一个updateQueue队列来缓存等待更新的状态和更新操作,用到了一些异步更新和批量更新的技巧,以提高组件的性能。
总结为以下几个步骤:
- 在useState函数中,定义了一个名为currentlyRenderingComponent的变量,用于存放当前正在渲染的组件信息。
- 在useState函数中,首先判断当前是否正在进行批量更新,如果是,直接使用上次的更新值;否则,就根据参数initialState生成新的状态值和状态更新函数并返回。
- 定义了一个createDispatcher函数,用于创建新的状态更新函数。它首先定义了一个value变量存放当前状态的值,并根据传入的新状态值来更新它,然后调用scheduleRender函数告知React需要进行重新渲染。
- 定义了scheduleRender函数,用于触发组件的重新渲染。如果当前正在批量更新状态,则不会立即进行更新,而是先将所有更新操作加入pending队列中,等待所有的更新操作处理完毕后,一起进行更新。
- 定义了一个名为setState的更新函数,它接收两个参数,第一个参数为待更新的状态的索引值,第二个参数为新的状态值。在setState函数中,首先更新pending队列中对应的元素值,然后调用scheduleRender函数触发组件的重新渲染。
// 源码
let currentlyRenderingComponent;
function useState(initialState) {
const component = currentlyRenderingComponent || {
updaterQueue: {
pending: [], // 更新队列,用于存放所有等待更新的 setter
batching: false // 指示是否处于批量更新模式
}
};
const {updaterQueue} = component; // 获取更新队列
// 如果正在处于批量更新模式,则使用上一次的值,否则使用传入的初始值
const currentIndex = updaterQueue.pending.length;
if (currentIndex) {
return [updaterQueue.pending[currentIndex - 1].value, setState.bind(null, currentIndex)];
} else {
const [val, dispatch] = createDispatcher(initialState);
return [val, dispatch];
}
function setState(index, newState) {
// 使用updaterQueue更新pending队列中的值
updaterQueue.pending[index].value = newState;
try {
scheduleRender(component);
} catch (err) {
console.log(err);
}
}
}
// 创建状态更新函数
function createDispatcher(initialState) {
let value = initialState;
const dispatch = newState => {
value = typeof newState === "function" ? newState(value) : newState;
try {
const component = currentlyRenderingComponent;
scheduleRender(component); // 触发重新渲染
} catch (err) {
console.log(err);
}
};
return [value, dispatch];
}
// 触发重新渲染
function scheduleRender(component) {
// 如果正在进行批量更新,则不会立即更新,等待队列中所有的setter处理完后,统一进行更新
if (component.updaterQueue.batching) return;
component.updaterQueue.batching = true;
Promise.resolve().then(() => {
let { updaterQueue } = component;
if (!updaterQueue.pending.length) {
component.updaterQueue.batching = false;
return;
}
// 批量更新状态
let i = 0;
while (i < updaterQueue.pending.length) {
const current = updaterQueue.pending[i];
current.execute();
i++;
}
// 清空pending队列,继续进行下一次更新
updaterQueue.pending = [];
component.updaterQueue.batching = false;
});
}