七天快速学完mini-react ,再也不担心不会原理了(第六天)

# 七天快速学完mini-react ,再也不担心不会原理了(第一天)

# 七天快速学完mini-react ,再也不担心不会原理了(第二天)

# 七天快速学完mini-react ,再也不担心不会原理了(第三天)

# 七天快速学完mini-react ,再也不担心不会原理了(第四天)

# 七天快速学完mini-react ,再也不担心不会原理了(第五天)

第六天:搞定 useState

实现 useState

我们先写一个demo

jsx 复制代码
import React from "./core/React.js"
function Foo() {
  const [count, setCount] = React.useState(10)
  function handleClick() {
    setCount(pre => pre + 2)
  }
  return (
    <div>
      <h1>Foo : {count}</h1>
      <button onClick={handleClick}>click</button>
    </div>
  )
}
function App() {
  return (
    <div>
      <h1>App</h1>
      <Foo></Foo>
    </div>
  )
}

export default App

这里的话,我们先实现通过函数去实现数据更新

js 复制代码
function useState(initial) {
  let currentFiber = wipFiber
  let oldHook = currentFiber.alternate?.stateHook

  const stateHook = {
    state: oldHook ? oldHook.state : initial,
  }

  currentFiber.stateHook = stateHook

  function setState(action) {

    stateHook.state = action(stateHook.state)

    wipRoot = {
      ...currentFiber,
      alternate: currentFiber,
    }

    nextWorkOfUnit = wipRoot
  }

  return [stateHook.state, setState]
}

在函数内部,首先获取当前的Fiber节点currentFiber,然后尝试获取之前的钩子状态oldHook,如果存在的话。接着创建一个stateHook对象,其中的state属性被初始化为之前的状态或者初始值initial

然后将stateHook对象赋值给currentFiberstateHook属性。接下来定义了setState函数,它接受一个action作为参数,这个action是一个函数,用于根据当前状态计算新的状态。在setState函数内部,就是之前的update函数了。

最后,useState函数返回一个数组,其中第一个元素是状态的当前值,第二个元素是setState函数,用于更新状态。

我们可以看到,确实更新了

但是呢,如果我们写了多个useState,就会出现问题,因为我们的oldHook是一个变量,所以我们需要用数组来存储

jsx 复制代码
import React from "./core/React.js"
function Foo() {
  const [count, setCount] = React.useState(10)
  const [bar, setBar] = React.useState("bar")
  function handleClick() {
    setCount(pre => pre + 2)
    setBar(pre => pre + "bar")
  }
  return (
    <div>
      <h1>Foo : {count}</h1>
      <div>{bar}</div>
      <button onClick={handleClick}>click</button>
    </div>
  )
}
function App() {
  return (
    <div>
      <h1>App</h1>
      <Foo></Foo>
    </div>
  )
}

export default App
js 复制代码
let stateHooks
let stateHookIndex
function useState(initial) {
  let currentFiber = wipFiber
  let oldHook = currentFiber.alternate?.stateHooks[stateHookIndex]

  const stateHook = {
    state: oldHook ? oldHook.state : initial,
  }
  stateHookIndex++
  stateHooks.push(stateHook)
  currentFiber.stateHooks = stateHooks

  function setState(action) {
    stateHook.state = action(stateHook.state)

    wipRoot = {
      ...currentFiber,
      alternate: currentFiber,
    }

    nextWorkOfUnit = wipRoot
  }

  return [stateHook.state, setState]
}

我们这里通过设置stateHooks变量去存储stateHook,并且设置stateHookIndex索引来获取老的值,这样就不会影响下次更新了,这也是为什么useState必须写在顶层,不能用if语句去包裹的原因,

这里需要注意的是,每次更新后,需要把值清空

js 复制代码
function updateFunctionComponent(fiber) {
  stateHooks = []
  stateHookIndex = 0
  wipFiber = fiber
  const children = [fiber.type(fiber.props)]

  reconcileChildren(fiber, children)
}

这样一来我们就已经完成了useState

批量执行 action

上一节我们写的方法,其实是每次触发useStateaction的时候,都会更新一下视图,这样是不太好的,会造成性能上的浪费,所以,这一节我们来实现一下useState的批处理

js 复制代码
let stateHooks
let stateHookIndex
function useState(initial) {
  let currentFiber = wipFiber
  let oldHook = currentFiber.alternate?.stateHooks[stateHookIndex]

  const stateHook = {
    state: oldHook ? oldHook.state : initial,
    queue: oldHook ? oldHook.queue : [],
  }
  // 调用action
  stateHook.queue.forEach(action => {
    stateHook.state = action(stateHook.state)
  })
  stateHook.queue = []

  stateHookIndex++
  stateHooks.push(stateHook)
  currentFiber.stateHooks = stateHooks

  function setState(action) {
    stateHook.queue.push(typeof action === "function" ? action : () => action)
    // stateHook.state = action(stateHook.state)

    wipRoot = {
      ...currentFiber,
      alternate: currentFiber,
    }

    nextWorkOfUnit = wipRoot
  }

  return [stateHook.state, setState]
}

这里我们加入一个queue来存储action,并循环去执行action,这样就实现了把多次action的操作,转化成一次去执行。

我们还去判断了一下action的类型,如果不是函数,那么我们就包装成一个函数,这样我们就实现了直接输入值的情况。

提前检测-减少不必要的更新

当值没有发生改变的时候,我们应该不需要去更新组件

js 复制代码
import React from "./core/React.js"
function Foo() {
  const [count, setCount] = React.useState(10)
  const [bar, setBar] = React.useState("bar")
  function handleClick() {
    setBar(pre => "bar")
  }
  return (
    <div>
      <h1>Foo : {count}</h1>
      <div>{bar}</div>
      <button onClick={handleClick}>click</button>
    </div>
  )
}
function App() {
  return (
    <div>
      <h1>App</h1>
      <Foo></Foo>
    </div>
  )
}

export default App

我们只需要去判断一下值是否相等就行了!!!

js 复制代码
let stateHooks
let stateHookIndex
function useState(initial) {
  let currentFiber = wipFiber
  let oldHook = currentFiber.alternate?.stateHooks[stateHookIndex]

  const stateHook = {
    state: oldHook ? oldHook.state : initial,
    queue: oldHook ? oldHook.queue : [],
  }
  // 调用action
  stateHook.queue.forEach(action => {
    stateHook.state = action(stateHook.state)
  })
  stateHook.queue = []

  stateHookIndex++
  stateHooks.push(stateHook)
  currentFiber.stateHooks = stateHooks

  function setState(action) {
    // 处理值一样的情况
    const eagerState = typeof action === "function" ? action(stateHook.state) : action
    if (eagerState === stateHook.state) return

    stateHook.queue.push(typeof action === "function" ? action : () => action)
    // stateHook.state = action(stateHook.state)

    wipRoot = {
      ...currentFiber,
      alternate: currentFiber,
    }

    nextWorkOfUnit = wipRoot
  }

  return [stateHook.state, setState]
}

到目前为止,我们已经完成了useState的方法了,下一期将进入useEffect的学习

相关推荐
陈随易9 小时前
MoonBit抓包模块pcap,查看电脑的每一次联网通信
前端·后端·程序员
新中地GIS开发老师10 小时前
WebGIS开发学生作品|低空航天管理与航线规划系统
前端·javascript·webgis·三维gis开发
用户0595401744610 小时前
大模型对话记忆持久化踩坑实录:用 Playwright 自动化测了 300 次,终于揪出会话丢失的真凶
前端·css
丙氨酸長鏈10 小时前
Web前端入门第 问:JavaScript 一个简单的 IndexedDB 数据库入门示例
前端·javascript·数据库
kyriewen11 小时前
面试官让我手写虚拟列表——AI生成的版本,快速滚动几下就白屏了
前端·javascript·面试
IT_陈寒11 小时前
Vite热更新失效?你可能漏了这个配置
前端·人工智能·后端
梅孔立12 小时前
两种免费的翻译API调用方式详解 - Edge官方API与个人缓存服务
前端·缓存·edge
Revolution6112 小时前
React 组件重新渲染时,到底重新执行了什么
前端·react.js·面试
林焱_RPAAI12 小时前
影刀RPA技术深度:CSS选择器高级实战指南——伪类属性选择器与性能对比完全解析
vue.js·react.js
用户21816970493013 小时前
Flutter(四)Dart语法 空安全 运算符 流程控制
前端