六、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閱讀 1,735評論 0 1
  • 摘要: React示例教程。 原文:快速了解 React Hooks 原理 譯者:前端小智 到 React 16....
    Fundebug閱讀 6,198評論 0 2
  • 首先創建一個App組件,加入一個按鈕和點擊后顯示的值num,在按鈕上綁定click事件,每次點擊,num++ 在首...
    西域戰神閱讀 7,692評論 1 2
  • 函數組件 function App() { const [num, updateNum] = useState(...
    sharon_007閱讀 1,838評論 0 0
  • 示例代碼 注意 1 .setState 和上次相同的值,是不會觸發組件渲染的2 .useState 只會在初始化代...
    skoll閱讀 778評論 0 0