Loading...

# react 简易版 useState 实现方式

# 前言:

reacthooks 是应用的很常见的一个技术,而 useState 更是一个很常见的 hook , 这里介绍其实现方式。写法实际上是 react 源码的简化版。

# 思路:

​ 那么首先我们需要知道 hooks 的基础用法。对于 useState 来说比较常见的用法是

function APP(){
	const [val,setVal]=useState(initialVal);
    ...
    setVal((val)=>{
        ...
    })
}

​ 为了方便起见,我们采用了简单的 isMount 来记录组件是初次加载还是重新渲染,利用 workInProgressHook 来记录当前工作的 hooks , 利用 schedule 函数来进行调度重新渲染

let isMount = true;
let workInProgressHook = null;
const fiber = {
  stateNode: APP,
  // 保存函数组件 hooks 上的 state,class 组件中上的 (链表)
  memoizedState: null,
};
function schedule() {
  workInProgressHook = fiber.memoizedState;
  const testRes = fiber.stateNode();
  isMount = false;
  // 便于看结果将其返回
  return testRes;
}
function APP() {
  const [count, setCount] = useState(0);
  const [num,setNum]=useState(10);
  console.log(isMount?'APP初始化':'重新加载','count对应的值是',count,'num对应的值是',num)
  return {
    addOne() {
      setCount(
        (count)=>{
       return  count + 1;
        });
    },
    addTen() {
        setNum(
          (num)=>{
         return  num + 10;
          });
      },
  };
}
function useState(initialState) {
    //TODO 补全内容。
}
window.app = schedule();

​ 由于 useState 可能会调用多次,所以必须要将其存储起来,才能保证每次获得的值是一一对应。 react 采用的是链表的方式进行存储的,每个组件都有一个 fiber 与之对应, fiber 上的 memoizedState 就是对应存储的 state

​ 对于 useState 来说,需要返回当前的值,为了记录这个值,采用了 memoizedState 进行记录,当初次加载时创建一个 hook , 其中包含初始值,和修改操作列表。

​ 然后将其挂在 fiber.memoizedState 的尾部。需要读取的时候,遍历链表获取对应的值。

function useState(initialState) {
  let hook;
  if (isMount) {
    hook = {
      memoizedState: initialState, // 初始值
      next: null,
      queue:{					   // 对应的更改的操作
          pending:null
      }
    };
    // 是第一个 hook 的话,进行赋值
    if (!fiber.memoizedState) {
      fiber.memoizedState = hook;
    } else {
      // 记录到尾部
      workInProgressHook.next = hook;
    }
    // 记录当前链表的尾部
    workInProgressHook = hook;
  } else {
    hook = workInProgressHook;
    workInProgressHook = workInProgressHook.next;
  }
  // 获取初始状态
  let baseState=hook.memoizedState;
  //TODO , 返回修改值的函数
   ...
  return [baseState,fun()];

然后就是具体修改值的函数,如下所示,因为修改的函数可能会多次执行,所以其存储结构采用的是环状列表来实现。对应于上面的 queue

hook = {
      memoizedState: initialState, // 初始值
      next: null,
      queue:{					   // 对应的更改的操作
          pending:null
      }
    };

其每一项都包含一个我们传入的 action

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.next=update;
    }
    // 指向该链表的最后一个元素
    queue.pending=update;
    // 触发更新
    schedule();
}

完整的代码如下图所示

let isMount = true;
let workInProgressHook = null;
const fiber = {
  stateNode: APP,
  // 保存函数组件 hooks 上的 state,class 组件中上的 (链表)
  memoizedState: null,
};
function schedule() {
  workInProgressHook = fiber.memoizedState;
  const testRes = fiber.stateNode();
  isMount = false;
  // 便于看结果给一个返回值
  return testRes;
}
function APP() {
  const [count, setCount] = useState(0);
  const [num,setNum]=useState(10);
  console.log(isMount?'APP初始化':'重新加载','count对应的值是',count,'num对应的值是',num)
  return {
    addOne() {
      setCount(
        (count)=>{
       return  count + 1;
        });
    },
    addTen() {
        setNum(
          (num)=>{
         return  num + 10;
          });
      },
  };
}
function useState(initialState) {
  let hook;
  if (isMount) {
    hook = {
      memoizedState: initialState,
      next: null,
      queue:{
          pending:null
      }
    };
    // 是第一个 hook 的话,进行赋值
    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 firstAction=firstUpdate.action;
        console.log('baseState',baseState,firstAction,firstAction(baseState))
        baseState=firstAction(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.next=update;
    }
    // 指向该链表的最后一个元素
    queue.pending=update;
    // 触发更新
    schedule();
}
window.app = schedule();

简易版useState演示

useState 演示地址

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

jluyeyu 微信支付

微信支付

jluyeyu 支付宝

支付宝