七天快速学完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的学习

相关推荐
whisperrr.3 分钟前
【JavaWeb12】数据交换与异步请求:JSON与Ajax的绝妙搭配是否塑造了Web的交互革命?
前端·ajax·json
烂蜻蜓1 小时前
前端已死?什么是前端
开发语言·前端·javascript·vue.js·uni-app
Rowrey2 小时前
react+typescript,初始化与项目配置
javascript·react.js·typescript
谢尔登2 小时前
Vue 和 React 的异同点
前端·vue.js·react.js
祈澈菇凉6 小时前
Webpack的基本功能有哪些
前端·javascript·vue.js
小纯洁w6 小时前
Webpack 的 require.context 和 Vite 的 import.meta.glob 的详细介绍和使用
前端·webpack·node.js
想睡好7 小时前
css文本属性
前端·css
qianmoQ7 小时前
第三章:组件开发实战 - 第五节 - Tailwind CSS 响应式导航栏实现
前端·css
zhoupenghui1687 小时前
golang时间相关函数总结
服务器·前端·golang·time
White graces8 小时前
正则表达式效验邮箱格式, 手机号格式, 密码长度
前端·spring boot·spring·正则表达式·java-ee·maven·intellij-idea