Re: 新手让网页 act 起来: Day13 - Hook Flow

前言

昨天在介绍 useEffect 的时候有提到 useEffect 中的执行顺序,今天就再做进一步的观察,来加深对於 useEffect 在 Component mount、 update 与 unmount 三个状况下的执行时机吧!

范例

建立 App 与 Child 元件,并在 App 元件中设立 showChild 的 state 来控制要不要显示 Child 元件。并且在两个元件中都设置三个 useEffect 分别是没有 dependency array、空阵列与放置一个 state 的 dependency array。

const Child = () => {
  console.log('%c    Child: render start', 'color: MediumSpringGreen')
  const [count, setCount] = useState(()=> {
    console.log('%c    Child: useState(() => 0)', 'color: tomato')
    return 0
  })

  useEffect(() => {
    console.log('%c    Child: useEffect(() => {})', 'color: LightCoral')
    return () => {
      console.log(
        '%c    Child: useEffect(() => {}) cleanup',
        'color: LightCoral',
      )
    }
  })

  useEffect(() => {
    console.log(
      '%c    Child: useEffect(() => {}, [])',
      'color: MediumTurquoise',
    )
    return () => {
      console.log(
        '%c    Child: useEffect(() => {}, []) cleanup',
        'color: MediumTurquoise',
      )
    }
  }, [])

  useEffect(() => {
    console.log('%c    Child: useEffect(() => {}, [count])', 'color: HotPink')
    return () => {
      console.log(
        '%c    Child: useEffect(() => {}, [count]) cleanup',
        'color: HotPink',
      )
    }
  }, [count])

  const increaseHandler = () => {
    setCount(prev => prev + 1)
  }

  console.log('%c    Child: render end', 'color: MediumSpringGreen')
  return (
    <button className="plus" onClick={increaseHandler}>{count}</button>
  )
};

function App() {
  console.log('%cApp: render start', 'color: red')

  const [showChild, setShowChild] = useState(()=>{
    console.log('%cLazy initialize', 'color: MediumSpringGreen')

    return false
  })

  function onChangeHandler(){
    setShowChild(prev => !prev)
  }

  useEffect(() => {
    console.log('%cApp: useEffect(() => {})', 'color: LightCoral')
    return () => {
      console.log('%cApp: useEffect(() => {}) cleanup', 'color: LightCoral')
    }
  })

  useEffect(() => {
    console.log('%cApp: useEffect(() => {}, [])', 'color: MediumTurquoise')
    return () => {
      console.log(
        '%cApp: useEffect(() => {}, []) cleanup',
        'color: MediumTurquoise',
      )
    }
  }, [])

  useEffect(() => {
    console.log('%cApp: useEffect(() => {}, [showChild])', 'color: HotPink')
    return () => {
      console.log(
        '%cApp: useEffect(() => {}, [showChild]) cleanup',
        'color: HotPink',
      )
    }
  }, [showChild])

  console.log('%cApp: render end', 'color: red')
  return (
    <div className="App">
      <input type='checkbox' value={showChild} onChange={onChangeHandler} />
      <div>
        { showChild ? <Child /> : null}
      </div>
    </div>
  );
}

画面与功能会如下

接下来我们来观察一下console.log 会在浏览器的 console 印出什麽吧!

  1. 刚载入 App 时 hooks 中的

从印出的结果可以发现 useState 中的 Lazy initialize 会在元件开始呼叫之後马上执行,而 useEffect 会在 render 之後开始依照顺序执行。

  1. 当我们按下 input 之後,让 Child 元件显示

按下 input 会更新 App 中的 showChild state ,并 re-render,那 Lazy initialize 就不会再触发了。之後开始呼叫 Child 元件,一样会先执行 useState 的 Lazy initialize 。

接下来值得注意的是,因为 App 元件是 update 的 re-render 所以会触发之前注册的 useEffect cleanup function。然後执行完之後会先从子层的 useEffect 开始执行,再执行父层的 useEffect。

  1. 按下 Child 的 button 更新 count

更新 Child 元件的 count state 只会影响 Child 元件,并不会影响到 父层,所以 Child 触发 re-render 然後先执行 useEffect cleanup function 再执行 useEffect 。

  1. 再次按下 input unmount Child 元件

再次按下 input 之後,State 更新触发 App re-render ,然後因为 Child 元件 unmount 所以会触发 Child 的 useEffect cleanup function,然後再执行 App 的 useEffect cleanup function ,接者执行 useEffect 。

最後我们可以针对 Mount、Update 与 Unmount 做个小小的整理:

Mount

  1. 执行 Lazy initialize
  2. React render
  3. React 更新 DOM
  4. 浏览器渲染画面
  5. 执行 useEffect

Update

  1. React render
  2. React 更新 DOM
  3. 浏览器渲染画面
  4. 执行 useEffect 的 cleanup function
  5. 执行 useEffect

Unmount

  1. 执行 useEffect 的 cleanup function

最後的最後关於比较完整的 hooks flow 的图表可以看 Donavon 的 Github hook-flow

该文章同步发布於:我的部落格


<<:  Swift 新手-ios 应用软件开发资料与云端储存篇(二)

>>:  【Day13】Latch 的生成条件以及如何避免(下)

战略层次(Levels of Strategy)

-战略层次 通常,CEO 负责制定公司战略或大战略,董事会的意见和高级管理团队的支持。 CISO ...

[Day8] THM Bolt

今天再来试玩一个 Try Hack Me 上面的简单题目,攻打一个 CMS (Content Man...

Day4 专案架构

之後会以create-react-app建立的专案,来进行React後续的学习, 来看看建立後专案的...

Day 8:OkHttp 获取网路资料方式

本篇文章同步发表在 HKT 线上教室 部落格,线上影音教学课程已上架至 Udemy 和 Youtu...

从零开始学游戏设计:使用粒子做爆炸效果

这是 Roblox 从零开始系列,效果章节的第八个单元,今天将说明如何透过粒子来实作爆炸效果 Par...