react hooks中的useState如何实现的

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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值