六、useState

用法

const [count, setCount] = useState(0)

const handleClick = () => {
  setCount((count) => count + 1)
}

实现:

  1. 要返回一个数组,第一个是当前的 state,第二个是修改 state 的方法
const useState = (inital) => {
  const stateHook = {
    state: inital
  }

  const setState = (action) => {
    stateHook.state = action(stateHook.state)
  }

  return [stateHook.state, setState]
}
  1. 更新页面使用直接的 update 里的逻辑修改 wipRoot
  let currentFiber = wipFiber


  const setState = (callback) => {
    stateHook.state =  callback(stateHook.state)
    wipRoot = {
      ...currentFiber,
      alternate: currentFiber
    }
    nextWorkOfUnit = wipRoot
  }
  1. 因为每次我们更新页面我们的state 的值都会重置所以我们需要存储我们的 state 的值,存在我们之前的 fiber 上
const useState = (state) => {
  let currentFiber = wipFiber
  const oldHook = currentFiber.alternate?.stateHook
  const stateHook = {
    state: oldHook ? oldHook.state : state
  }

  currentFiber.stateHook = stateHook

  const setState = (callback) => {
    stateHook.state =  callback(stateHook.state)
    wipRoot = {
      ...currentFiber,
      alternate: currentFiber
    }
    nextWorkOfUnit = wipRoot
  }

  return [stateHook.state, setState]
}
  1. 实现多个 useState
    上面的 useState 如果我们页面里有多个后面的会把前面的覆盖
  • demo
const Foo = () => {
  console.log('foo render')
  const [count, setCount] = React.useState(0)
  const [age, setAge] = React.useState(1)
  const handleClick = () => {
    setCount((count) => count + 1)
    setAge((age) => age + 2)
  }
  return (
    <div>
      <h1>foo</h1>
      {count}
      {age}
      <button onClick={handleClick}>click</button>
    </div>
  )
}

点击都会每次 +2
解决方式:使用一个数组和索引来对应关系
每次updateFunctionComponent 的时候初始化数组和索引值

let stateHooks
let stateHooksIndex
const updateFunctionComponent = (fiber) => {
+  stateHooks = []
+  stateHooksIndex = 0
  wipFiber = fiber
  const children = [fiber.type(fiber.props)]
  initChildren(fiber, children)
}

const useState = (inital) => {
  let currentFiber = wipFiber
  const oldHook = currentFiber.alternate?.stateHooks[stateHooksIndex]
  const stateHook = {
    state: oldHook ? oldHook.state : inital
  }

  stateHooks.push(stateHook)
  currentFiber.stateHooks = stateHooks

  const setState = (action) => {
    stateHook.state = action(stateHook.state)
    wipRoot = {
      ...currentFiber,
      alternate: currentFiber
    }
    nextWorkOfUnit = wipRoot
  }
  stateHooksIndex++
  return [stateHook.state, setState]
}
  1. 批量处理 action
const useState = (inital) => {
  let currentFiber = wipFiber
  const oldHook = currentFiber.alternate?.stateHooks[stateHooksIndex]
  const stateHook = {
    state: oldHook ? oldHook.state : inital,
    queue: oldHook? oldHook.queue : []
  }
  stateHook.queue.forEach((action) => stateHook.state = action(stateHook.state))
  stateHook.queue = []
  stateHooks.push(stateHook)
  currentFiber.stateHooks = stateHooks

  const setState = (action) => {
    stateHook.queue.push(action)
    wipRoot = {
      ...currentFiber,
      alternate: currentFiber
    }
    nextWorkOfUnit = wipRoot
  }
  stateHooksIndex++
  return [stateHook.state, setState]
}

每次steState 不会直接修改,是在dom重新渲染后才修改

  1. 支持直接传入值
    setAge(4)

  const setState = (action) => {
 +   stateHook.queue.push(typeof action === 'function' ? action : () => action)
...
}
  1. 减少不必要的更新
    我们上面的代码不管我们后面修改的 state和之前一不一样都不重新 render
  const setState = (action) => {
    const eagerState = typeof action === 'function' ? action(stateHook.state) : action
    if (eagerState === stateHook.state) {
      return
    }
  ....
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • useState用法 点击button会发生什么? 分析: setNsetN一定会修改数据x, 将n+1存入xse...
    sweetBoy_9126阅读 5,636评论 0 1
  • 摘要: React示例教程。 原文:快速了解 React Hooks 原理 译者:前端小智 到 React 16....
    Fundebug阅读 11,324评论 0 2
  • 首先创建一个App组件,加入一个按钮和点击后显示的值num,在按钮上绑定click事件,每次点击,num++ 在首...
    西域战神阅读 12,270评论 1 2
  • 函数组件 function App() { const [num, updateNum] = useState(...
    sharon_007阅读 5,860评论 0 0
  • 示例代码 注意 1 .setState 和上次相同的值,是不会触发组件渲染的2 .useState 只会在初始化代...
    skoll阅读 4,239评论 0 0