react useState如何实现的
import { useState } from 'react';
export function User() {
const [name, setName] = useState('');
const [age, setAge] = useState(18);
const onChangeName = (e) => {
setName(e.target.value);
}
const onChangeAge = (e) => {
setAge(Number(e.target.value));
}
return (<>
<input onChange={onChangeName}/>
<input onChange={onChangeAge} />
</>);
}
这个User 方法会在修改中多次执行,useState是如何保存数据
fiber
react中每个组件都在虚拟dom(fiber)
现在假设有一个fiber节点
interface IPending {
action: (val: any) => void;
next: IPending:
}
interface IMemoizedState {
memoizedState: any; // useState存的值
next: IMemoizedState; // 下一个useState
queue: {
pending: IPending ; // 当前节点的dispatch,
}
}
interface IFiber {
stateNode: any; // 这个节点生成方法
memoizedState: IMemoizedState; // 用到的hooks,这个单指useState
isMount: boolean; // 是否挂载了
workInProgresssHooks: IMemoizedState; //当前执行到的hook(useState)
}
const fiber = {
stateNode: User,
memoizedState: null,
isMount: true,
workInProgresssHooks: null
}
重点
useState
在fiber上。useState等执行其实是保存到了一个单向链表上,workInProgresssHooks保存当前的执行点,memoizedState保存这个链表的头,每次调度执行时会将workInProgresssHooks重置到header;
因为是链表执行顺序非常重要,所有官方文档都说了在使用use的hook不能添加if的逻辑判断。
修改useState的返回值呢
因为如在react中如setAge即dispatch等方法都是异步,调用之后并不会立即执行更新,而是等调度算法来更新,所以修改时会将,执行操作放在useState产生的memoizedState.queue.pending中,这个也是一个链表,但是并没有每一次dispatch都会创建一个节点,这个并未保存链表的header,所以这个链表是一个环形链表,最后一个节点指向链表的头
大概的代码实现
HOOKS的组件返回一个对象模拟返回jsx
function App() {
const [num, updateNum] = useState(0);
const [num1, updateNum1] = useState(100);
const [num2, updateNum2] = useState(1000);
console.log('isMount:', fiber.isMount);
console.log(num, num1, num2);
return {
onClick: () => {
updateNum((num1: any) => num1 + 1);
updateNum((num1: any) => num1 + 1);
updateNum1((x: number) => x + 100);
updateNum2((x: number) => x + 1000);
}
}
}
fiber节点
const fiber: IFiber= {
stateNode: App,
memoizedState: null,
isMount: true,
workInProgresssHooks: null
}
挂载
function schedule() {
fiber.workInProgresssHooks = fiber.memoizedState;
const app = fiber.stateNode();
fiber.isMount = false;
return app;
}
const app = schedule();
/**
模拟触发变更值app.onClick();
*/
useState弱鸡实现
function useState(initialState: number): [number, any] {
let hook: IMemoizedState;
if(fiber.isMount) { // 组件第一次执行时都是空的,即还未挂载时为空,创建hook链表
hook = {
memoizedState: initialState,
next: null,
queue: {
pending: null
}
}
if(!fiber.memoizedState) { // 执行第一个useState
fiber.memoizedState = hook;
} else { // 之后的state
fiber.workInProgresssHooks.next = hook;
}
// 存储执行的最后一个hook
fiber.workInProgresssHooks = hook;
} else {
hook = fiber.workInProgresssHooks;
fiber.workInProgresssHooks = fiber.workInProgresssHooks.next;
}
let baseState = hook.memoizedState;
if(hook.queue.pending) {//执行数据操作如setAge
//获取当前useState中set值操作的链表的头,挨个执行
let firstUpdate = hook.queue.pending.next;
do{
const action = firstUpdate.action;
baseState = action(baseState);
firstUpdate = firstUpdate.next;
} while(firstUpdate !== hook.queue.pending.next)
}
hook.queue.pending = null;
hook.memoizedState = baseState;
// dispatchAction.bind(null, hook.queue)
// 传递diapach的最后一个执行节点
return [baseState, dispatchAction.bind(null, hook.queue)];
}
dispatch
如setAge(age => age+1)
interface IActionNode {
action: (val: any) => void; // 这目前只写了setAge中为function的情况
next: IActionNode;
}
let timer: any = null;
function dispatchAction(queue: {pending: IActionNode}, action: any) {
// 创建创建环形链表
const update: IActionNode = {
action: action,
next: null
}
if(queue.pending === null) {
update.next = update
} else {
update.next = queue.pending.next;
queue.pending.next = update;
}
queue.pending = update;
// TIMEOUT 模拟dispatch异步执行
if(timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(() => schedule(),100);
}