弑君者

V1

2022/04/13阅读:67主题:默认主题

手把手带你了解useState原理

useState 是什么?如何实现?

react hooks 已经出来很长时间了,相信很多人都使用过,比如useState,useEffect等。

但是我们有没有想过useState的原理是什么?假如让你实现useState,你会如何实现呢?

大家可以先思考些。

我一直相信一句话,假如你不能创造它,你就根本没有了解它!!!

useState的原理

useState的本质是什么?如果除去react相关的一些乱七八糟附加信息,那么useState的本质是什么呢?

说白了就是在一个函数app里边调用useState,比如第一次调用的时候,执行

const [num,updateNum] = useState(1);

得到num等于1

然后当调用updateNum的时候,比如如下调用的时候,收集依赖到数组arr中

updateNum((num)=>num+1);

执行完之后arr=[(num)=>num+1] 等到下次再执行app之后,这时候我们就会得到根据arr里边收集的依赖,得到新的num等于2.

所以useState的本质是什么呢?

就是一个可以保存状态,并且可以收集依赖,等到下次再执行的时候,根据依赖重新获取更新之后的状态的函数。

实现一个简单的useState

我们根据上面介绍,来实现一个简单的useState,把基本原理了解清楚,然后自己再去使用或者看源码,就会更清楚了。

我们采用自上而下的方法,先搭建好框架,然后再把需要的填进去就可以了。

  function app({
      const [num,updateNum] = myUseState(1);
      return {
        updateNum
      }
    }

这里我们知道在执行myUseState的时候,是需要保存上一次执行之后的状态的,所以我们需要两个变量。

// 用来保存上一次状态
    let fiber = {

    };
    // 区分是否调用过
    let hasCalled = false;

然后逻辑就比较简单了,我们在第一次执行函数的时候,创建一个对象挂载到fiber上,然后返回一个数组,当函数执行过之后,我们只需要更新状态就可以了。

 // 返回更新状态的函数,这里是挂载到全局fiber.hook.pending上
    function dispatch(pending,action{
      pending.action=action;
    }
    function myUseState(initState{
      // 还没有执行过函数
      if(!hasCalled) {
        // 创建一个对象
        const hook = {
          basicState: initState,
          pending: {
            action:null
          }
        }
        // 挂载到fiber上
        fiber.hook = hook;
        // 标记已经执行过
        hasCalled=true;
        // 返回一个数组
        return [hook.basicState,dispatch.bind(null,fiber.hook.pending)]
      }else {
        // 当已经执行过该函数,只需要更新状态就可以了
        fiber.hook.basicState = fiber.hook.pending.action(fiber.hook.basicState);
        // 返回数组
        return [fiber.hook.basicState,dispatch.bind(null,fiber.hook.pending)]
      }
    }

代码比较简单,完整的注释已经在代码里边了。

完整的代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    function app() {
      const [num,updateNum] = myUseState(1);
      console.log(111,num)
      return {
        updateNum
      }
    }
    // 用来保存上一次状态
    let fiber = {

    };
    // 区分是否调用过
    let hasCalled = false;
    // 返回更新状态的函数,这里是挂载到全局fiber.hook.pending上
    function dispatch(pending,action) {
      pending.action=action;
    }
    function myUseState(initState) {
      // 还没有执行过函数
      if(!hasCalled) {
        // 创建一个对象
        const hook = {
          basicState: initState,
          pending: {
            action:null
          }
        }
        // 挂载到fiber上
        fiber.hook = hook;
        // 标记已经执行过
        hasCalled=true;
        // 返回一个数组
        return [hook.basicState,dispatch.bind(null,fiber.hook.pending)]
      }else {
        // 当已经执行过该函数,只需要更新状态就可以了
        fiber.hook.basicState = fiber.hook.pending.action(fiber.hook.basicState);
        // 返回数组
        return [fiber.hook.basicState,dispatch.bind(null,fiber.hook.pending)]
      }
    }
    const myApp = app();
    myApp.updateNum((num)=>num+1);
    app()

  </script>

</body>
</html>

执行结果如下:

分类:

前端

标签:

前端

作者介绍

弑君者
V1