手写实现一个useState函数,主要考察链表在react中的应用
let isMount = true; // 申明一个全局变量,用来区分 mount 和 update
let workInProgressHook = null;
// 节点
const fiber = {
stateNode: App, // stateNode 用来保存当前组件
memoizedState: null, // 用来保存当前组件内部的状态
}
function schedule() {
// 每次运行时将当前fiber指向workProgressHook
workInProgressHook = fiber.memoizedState;
// 触发组件render
const app = fiber.stateNode();
isMount = false;
return app;
}
function useState(initState) {
...
}
function App() {
const [num, setNum] = useState(0);
return {
onClick() {
setNum(n => n + 1);
},
};
};
schedule().onClick()
完整版代码
let isMount = true; // 申明一个全局变量,用来区分 mount 和 update
let workInProgressHook = null; // 申明一个全局变量,作为链表的指针
const fiber = {
stateNode: App, // stateNode 用来保存当前组件
memoizedState: null, // 用来保存当前组件内部的状态
};
function useState(initialState) {
let hook;
if (isMount) {
hook = {
memoizedState: initialState,
next: null,
queue: {
pending: null,
}
};
if (!fiber.memoizedState) {
fiber.memoizedState = hook;
} else {
workInProgressHook.next = hook;
}
workInProgressHook = hook;
} else {
hook = workInProgressHook;
workInProgressHook = workInProgressHook.next;
}
let baseState = hook.memoizedState;
if (hook.queue.pending) {
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;
return [baseState, dispatchAction.bind(null, hook.queue)];
};
function dispatchAction(queue, action) {
const update = {
action,
next: null,
};
if (queue.pending === null) {
update.next = update;
} else {
update.next = queue.pending.next;
queue.pending.next = update;
}
queue.pending = update;
schedule();
};
function schedule() {
workInProgressHook = fiber.memoizedState; // 让指针指向当前的useState保存的值
const app = fiber.stateNode(); // 执行组件的渲染函数,将结果保存在app里
isMount = false; // 首次渲染之后,isMount 变成 false
return app; // 将fiber.stateNode的结果返回
};
function App() {
const [num, setNum] = useState(0);
console.log(num);
return {
onClick() {
setNum(n => n + 1);
},
};
};