React-hooks

1 hooks使命

#逻辑组件复用
  • 逻辑与UI组件分离

    React 官方推荐在开发中将逻辑部分与视图部分结耦,便于定位问题和职责清晰

  • 函数组件拥有state

    在函数组件中如果要实现类似拥有state的状态,必须要将组件转成class组件

  • 逻辑组件复用

社区一直致力于逻辑层面的复用,像 render props / HOC,不过它们都有对应的问题,Hooks是目前为止相对完美的解决方案

#hooks 解决的问题

render props

Avator 组件是一个渲染头像的组件,里面包含其中一些业务逻辑,User组件是纯ui组件,用于展示用户昵称

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">export</span> <span style="color:#cc99cd">default</span> funtion <span style="color:#f8c555">APP</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">return</span> <span style="color:#cccccc">(</span>
        <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>div</span> <span style="color:#e2777a">className</span><span style="color:#7ec699"><span style="color:#cccccc">=</span><span style="color:#cccccc">"</span>App<span style="color:#cccccc">"</span></span><span style="color:#cccccc">></span></span>
            <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span><span style="color:#f8c555">Avatar</span></span><span style="color:#cccccc">></span></span>
               <span style="color:#cccccc">{</span>data<span style="color:#67cdcc">=></span> <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span><span style="color:#f8c555">User</span></span> <span style="color:#e2777a">name</span><span style="color:#cccccc">=</span><span style="color:#cccccc">{</span>data<span style="color:#cccccc">}</span><span style="color:#cccccc">/></span></span><span style="color:#cccccc">}</span>
            <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span><span style="color:#f8c555">Avatar</span></span><span style="color:#cccccc">></span></span>
        <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span>div</span><span style="color:#cccccc">></span></span>
    <span style="color:#cccccc">)</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>
  • 通过渲染props来实现逻辑组件复用
  • render props 通过嵌套组件实现,在真实的业务中,会出现嵌套多层,以及梭理props不清晰的问题

Hoc

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">class</span> <span style="color:#f8c555">Avatar</span> <span style="color:#cc99cd">extends</span> <span style="color:#f8c555">Component</span> <span style="color:#cccccc">{</span>
    <span style="color:#f08d49">render</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">{</span>
        <span style="color:#cc99cd">return</span> <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>div</span><span style="color:#cccccc">></span></span><span style="color:#cccccc">{</span><span style="color:#cc99cd">this</span><span style="color:#cccccc">.</span>props<span style="color:#cccccc">.</span>name<span style="color:#cccccc">}</span><span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span>div</span><span style="color:#cccccc">></span></span>
    <span style="color:#cccccc">}</span>
<span style="color:#cccccc">}</span>
funtion <span style="color:#f08d49">HocAvatar</span><span style="color:#cccccc">(</span>Component<span style="color:#cccccc">)</span><span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">return</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span> <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span><span style="color:#f8c555">Component</span></span> <span style="color:#e2777a">name</span><span style="color:#7ec699"><span style="color:#cccccc">=</span><span style="color:#cccccc">'</span>王艺瑾<span style="color:#cccccc">'</span></span><span style="color:#cccccc">/></span></span>
<span style="color:#cccccc">}</span>
</code></span></span></span>
  • 通过对现有组件进行扩展、增强的方式来实现复用,通常采用包裹方法来实现
  • 高阶组件的实现会额外地增加元素层级,使得页面元素的数量更加臃肿

Hooks

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">import</span> React<span style="color:#cccccc">,</span><span style="color:#cccccc">{</span>useState<span style="color:#cccccc">}</span> <span style="color:#cc99cd">from</span> <span style="color:#7ec699">'react'</span>

<span style="color:#cc99cd">export</span> <span style="color:#cc99cd">function</span> <span style="color:#f08d49">HooksAvatar</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">const</span> <span style="color:#cccccc">[</span>name<span style="color:#cccccc">,</span>setName<span style="color:#cccccc">]</span><span style="color:#67cdcc">=</span><span style="color:#f08d49">useState</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'王一瑾'</span><span style="color:#cccccc">)</span>
    <span style="color:#cc99cd">return</span> <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span></span><span style="color:#cccccc">></span></span><span style="color:#cccccc">{</span>name<span style="color:#cccccc">}</span><span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span></span><span style="color:#cccccc">></span></span>
<span style="color:#cccccc">}</span>
</code></span></span></span>
  • React 16.8引入的Hooks,使得实现相同功能而代码量更少成为现实
  • 通过使用Hooks,不仅在编码层面减少代码的数量,同样在编译之后的代码也会更少

#2 hooks实践

#Hook官方APi(大概率用到的)
  • useState 函数组件中的state方法
  • useEffect 函数组件处理副作用的方法,什么是副作用?异步请求、订阅原生的dom实事件、setTimeoutd等
  • useContext 接受一个context对象(React.createContext的返回值)并返回该context的当前值,当前的context由上层组件中距离最近的<Mycontext.provider></Mycontext.provider>的value prop决定
  • useReducer 另一种"useState",跟redux有点类似
  • useRef 返回一个突变的ref对象,对象在函数的生命周期内一直存在
  • useMemo 缓存数值
  • useCallback 缓存函数
  • useCustom 自定义Hooks组件
  1. useState
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">import</span> React<span style="color:#cccccc">,</span><span style="color:#cccccc">{</span>useState<span style="color:#cccccc">}</span> <span style="color:#cc99cd">from</span> <span style="color:#7ec699">'react'</span>
<span style="color:#cc99cd">const</span> <span style="color:#f08d49">HooksTest</span> <span style="color:#67cdcc">=</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
    <span style="color:#999999">// 声明一个count的state变量,useState可以给一个默认值</span>
    <span style="color:#cc99cd">const</span> <span style="color:#cccccc">[</span>count<span style="color:#cccccc">,</span>setCount<span style="color:#cccccc">]</span><span style="color:#67cdcc">=</span><span style="color:#f08d49">useState</span><span style="color:#cccccc">(</span><span style="color:#f08d49">0</span><span style="color:#cccccc">)</span> 
    <span style="color:#999999">/*
        useState也可以传递一个函数,
            const [count,setCount]=useState(()=>{
            return 2
        })  
        setCount也可以传递一个函数
        这个函数第一个参数可以拿到上一次的值,
        在可以在函数里做一些操作
        setCount((preState)=>{
            return {...preState,..updatedValues}
        }) 
     */</span>
  <span style="color:#cc99cd">return</span> <span style="color:#cccccc">(</span>
        <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>div</span><span style="color:#cccccc">></span></span>
            <span style="color:#cccccc">{</span><span style="color:#999999">/*通过setCount来改变count的值*/</span><span style="color:#cccccc">}</span>
            <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>button</span> <span style="color:#e2777a">onClick</span><span style="color:#cccccc">=</span><span style="color:#cccccc">{</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
               <span style="color:#f08d49">setCount</span><span style="color:#cccccc">(</span>count<span style="color:#67cdcc">+</span><span style="color:#f08d49">1</span><span style="color:#cccccc">)</span> 
            <span style="color:#cccccc">}</span><span style="color:#cccccc">}</span>
            <span style="color:#cccccc">></span></span>Add<span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span>button</span><span style="color:#cccccc">></span></span>
            <span style="color:#cccccc">{</span>count<span style="color:#cccccc">}</span>
        <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>div</span><span style="color:#cccccc">></span></span>
    )
} 
</code></span></span></span>
  1. useEffect
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">import</span> React<span style="color:#cccccc">,</span><span style="color:#cccccc">{</span>useEffect<span style="color:#cccccc">}</span> <span style="color:#cc99cd">from</span> <span style="color:#7ec699">'react'</span><span style="color:#cccccc">;</span>
<span style="color:#999999">// 我们可以把useEffect 看做componentDidmount、componentDidUpdate、componntWillUnmount</span>
<span style="color:#cc99cd">const</span> <span style="color:#f08d49">HooksTest</span> <span style="color:#67cdcc">=</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">const</span> <span style="color:#cccccc">[</span>count<span style="color:#cccccc">,</span> setCount<span style="color:#cccccc">]</span> <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useState</span><span style="color:#cccccc">(</span><span style="color:#f08d49">0</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
    <span style="color:#999999">// useEffect可以让你在第一个参数的函数中执行副作用操作,就是请求数据,dom操作之类的</span>
    <span style="color:#999999">// useEffect返回一个函数,函数里表示要清除的副作用,例如清除定时器,返回的函数会在卸载组件时执行</span>
    <span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
        document<span style="color:#cccccc">.</span>title <span style="color:#67cdcc">=</span> <span style="color:#7ec699">`</span><span style="color:#7ec699">You clicked </span><span style="color:#cccccc">${</span>count<span style="color:#cccccc">}</span><span style="color:#7ec699"> times</span><span style="color:#7ec699">`</span><span style="color:#cccccc">;</span>
        <span style="color:#cc99cd">return</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
            <span style="color:#f08d49">clearInterval</span><span style="color:#cccccc">(</span>timer<span style="color:#cccccc">)</span>
        <span style="color:#cccccc">}</span>
    <span style="color:#cccccc">}</span><span style="color:#cccccc">)</span>
    <span style="color:#999999">/*
      useEffect的第二个参数,通过在数组中传递值,例如只有count变化时才调用Effect,达到
      不用每次渲染后都执行清理或执行effect导致的性能问题
    */</span>
    <span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
     document<span style="color:#cccccc">.</span>title <span style="color:#67cdcc">=</span> <span style="color:#7ec699">`</span><span style="color:#7ec699">You clicked </span><span style="color:#cccccc">${</span>count<span style="color:#cccccc">}</span><span style="color:#7ec699"> times</span><span style="color:#7ec699">`</span><span style="color:#cccccc">;</span>
    <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span><span style="color:#cccccc">[</span>count<span style="color:#cccccc">]</span><span style="color:#cccccc">)</span>

    <span style="color:#999999">/*
    如果想执行只运行一次的effect(仅在组件挂载和卸载时执行),可以传递一个空数组,
    告诉React你的Effect不依赖与props或state中任何值
    */</span>
    <span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
     document<span style="color:#cccccc">.</span>title <span style="color:#67cdcc">=</span> <span style="color:#7ec699">`</span><span style="color:#7ec699">You clicked </span><span style="color:#cccccc">${</span>count<span style="color:#cccccc">}</span><span style="color:#7ec699"> times</span><span style="color:#7ec699">`</span><span style="color:#cccccc">;</span>
    <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span><span style="color:#cccccc">[</span><span style="color:#cccccc">]</span><span style="color:#cccccc">)</span>

    <span style="color:#999999">/* 
      可以使用多个Effect,将不相关的逻辑分离到不同的effect中
    */</span>
   <span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
       axios<span style="color:#cccccc">.</span><span style="color:#f08d49">get</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'login'</span><span style="color:#cccccc">)</span>
   <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span><span style="color:#cccccc">[</span><span style="color:#cccccc">]</span><span style="color:#cccccc">)</span>
    <span style="color:#cc99cd">return</span><span style="color:#cccccc">(</span>
         <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>div</span><span style="color:#cccccc">></span></span>
            <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>p</span><span style="color:#cccccc">></span></span>You clicked <span style="color:#cccccc">{</span>count<span style="color:#cccccc">}</span> times<span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span>p</span><span style="color:#cccccc">></span></span>
            <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>button</span> <span style="color:#e2777a">onClick</span><span style="color:#cccccc">=</span><span style="color:#cccccc">{</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#f08d49">setCount</span><span style="color:#cccccc">(</span>count <span style="color:#67cdcc">+</span> <span style="color:#f08d49">1</span><span style="color:#cccccc">)</span><span style="color:#cccccc">}</span><span style="color:#cccccc">></span></span>
                Click me
            <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span>button</span><span style="color:#cccccc">></span></span>
         <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span>div</span><span style="color:#cccccc">></span></span>
    <span style="color:#cccccc">)</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>
  1. useContext
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#999999">// 1. 创建一个上下文管理组件context-manager.js,用于统一导出context实例</span>
<span style="color:#cc99cd">import</span> React <span style="color:#cc99cd">from</span> <span style="color:#7ec699">'react'</span>
<span style="color:#cc99cd">export</span> <span style="color:#cc99cd">const</span> ItemsContext <span style="color:#67cdcc">=</span> React<span style="color:#cccccc">.</span><span style="color:#f08d49">createContext</span><span style="color:#cccccc">(</span><span style="color:#cccccc">{</span> name<span style="color:#67cdcc">:</span> <span style="color:#7ec699">''</span> <span style="color:#cccccc">}</span><span style="color:#cccccc">)</span> <span style="color:#999999">//接受一个默认值</span>

<span style="color:#999999">// 2. 父组件提供数据</span>
<span style="color:#cc99cd">import</span> React <span style="color:#cc99cd">from</span> <span style="color:#7ec699">'react'</span>
<span style="color:#cc99cd">import</span> Child <span style="color:#cc99cd">from</span> <span style="color:#7ec699">'./child'</span>
<span style="color:#cc99cd">import</span> <span style="color:#cccccc">{</span> ItemsContext <span style="color:#cccccc">}</span> <span style="color:#cc99cd">from</span> <span style="color:#7ec699">'./context-manager'</span>
<span style="color:#cc99cd">import</span> <span style="color:#7ec699">'./index.scss'</span>

<span style="color:#cc99cd">const</span> items <span style="color:#67cdcc">=</span> <span style="color:#cccccc">{</span> name<span style="color:#67cdcc">:</span> <span style="color:#7ec699">'测试'</span> <span style="color:#cccccc">}</span>
<span style="color:#cc99cd">const</span> <span style="color:#f08d49">Father</span> <span style="color:#67cdcc">=</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">return</span> <span style="color:#cccccc">(</span>
    <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>div</span> <span style="color:#e2777a">className</span><span style="color:#7ec699"><span style="color:#cccccc">=</span><span style="color:#cccccc">'</span>father<span style="color:#cccccc">'</span></span><span style="color:#cccccc">></span></span>
      <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span><span style="color:#f8c555">ItemsContext.Provider</span></span> <span style="color:#e2777a">value</span><span style="color:#cccccc">=</span><span style="color:#cccccc">{</span>items<span style="color:#cccccc">}</span><span style="color:#cccccc">></span></span>
        <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span><span style="color:#f8c555">Child</span></span><span style="color:#cccccc">></span></span><span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span><span style="color:#f8c555">Child</span></span><span style="color:#cccccc">></span></span>
      <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span><span style="color:#f8c555">ItemsContext.Provider</span></span><span style="color:#cccccc">></span></span>
    <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span>div</span><span style="color:#cccccc">></span></span>
  <span style="color:#cccccc">)</span>
<span style="color:#cccccc">}</span>

<span style="color:#cc99cd">export</span> <span style="color:#cc99cd">default</span> Father

<span style="color:#999999">// 3.子组件用useContext解析上下文</span>
<span style="color:#cc99cd">import</span> React <span style="color:#cccccc">,</span><span style="color:#cccccc">{</span>useContext<span style="color:#cccccc">}</span> <span style="color:#cc99cd">from</span> <span style="color:#7ec699">'react'</span>
<span style="color:#cc99cd">import</span> <span style="color:#cccccc">{</span> ItemsContext <span style="color:#cccccc">}</span> <span style="color:#cc99cd">from</span> <span style="color:#7ec699">'./context-manager'</span>
<span style="color:#cc99cd">import</span> <span style="color:#7ec699">'./index.scss'</span>
<span style="color:#cc99cd">const</span> <span style="color:#f08d49">Child</span> <span style="color:#67cdcc">=</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> items<span style="color:#67cdcc">=</span><span style="color:#f08d49">useContext</span><span style="color:#cccccc">(</span>ItemsContext<span style="color:#cccccc">)</span>
  <span style="color:#cc99cd">return</span> <span style="color:#cccccc">(</span>
    <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>div</span> <span style="color:#e2777a">className</span><span style="color:#7ec699"><span style="color:#cccccc">=</span><span style="color:#cccccc">'</span>child<span style="color:#cccccc">'</span></span><span style="color:#cccccc">></span></span>
        子组件
        <span style="color:#cccccc">{</span>items<span style="color:#cccccc">.</span>name<span style="color:#cccccc">}</span>
    <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span>div</span><span style="color:#cccccc">></span></span>
  <span style="color:#cccccc">)</span>
<span style="color:#cccccc">}</span>
<span style="color:#cc99cd">export</span> <span style="color:#cc99cd">default</span> Child
</code></span></span></span>
  1. useReducer

useReducer是useState的替代方案,它接受一个形如(state,action)=>newState的reducer,并返回当前的state以及与其配套的dispatch方法

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">import</span> React<span style="color:#cccccc">,</span><span style="color:#cccccc">{</span>useReducer<span style="color:#cccccc">}</span> <span style="color:#cc99cd">from</span> <span style="color:#7ec699">'react'</span>
<span style="color:#cc99cd">const</span> initialState<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>count<span style="color:#67cdcc">:</span><span style="color:#f08d49">0</span><span style="color:#cccccc">}</span>
<span style="color:#cc99cd">function</span> <span style="color:#f08d49">reducer</span> <span style="color:#cccccc">(</span>state<span style="color:#cccccc">,</span>action<span style="color:#cccccc">)</span><span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">switch</span> <span style="color:#cccccc">(</span>action<span style="color:#cccccc">.</span>type<span style="color:#cccccc">)</span><span style="color:#cccccc">{</span>
        <span style="color:#cc99cd">case</span> <span style="color:#7ec699">'increment'</span><span style="color:#67cdcc">:</span>
            <span style="color:#cc99cd">return</span> <span style="color:#cccccc">{</span>count<span style="color:#67cdcc">:</span>state<span style="color:#cccccc">.</span>count<span style="color:#67cdcc">+</span><span style="color:#f08d49">1</span><span style="color:#cccccc">}</span>
        <span style="color:#cc99cd">case</span> <span style="color:#7ec699">'decrement'</span><span style="color:#67cdcc">:</span>
            <span style="color:#cc99cd">return</span> <span style="color:#cccccc">{</span>count<span style="color:#67cdcc">:</span>state<span style="color:#cccccc">.</span>count<span style="color:#67cdcc">-</span><span style="color:#f08d49">1</span><span style="color:#cccccc">}</span>
        <span style="color:#cc99cd">default</span><span style="color:#67cdcc">:</span>
            <span style="color:#cc99cd">throw</span> <span style="color:#cc99cd">new</span> <span style="color:#f8c555">Error</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span>
    <span style="color:#cccccc">}</span>

<span style="color:#cccccc">}</span>
<span style="color:#cc99cd">const</span> <span style="color:#cccccc">[</span>state<span style="color:#cccccc">.</span>dispatch<span style="color:#cccccc">]</span><span style="color:#67cdcc">=</span><span style="color:#f08d49">useReducer</span><span style="color:#cccccc">(</span>reducer<span style="color:#cccccc">,</span>initialState<span style="color:#cccccc">)</span>
<span style="color:#cc99cd">const</span> <span style="color:#f08d49">HooksTest</span> <span style="color:#67cdcc">=</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>

  <span style="color:#cc99cd">return</span> <span style="color:#cccccc">(</span>
        <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>div</span><span style="color:#cccccc">></span></span>
            <span style="color:#cccccc">{</span>state<span style="color:#cccccc">.</span>count<span style="color:#cccccc">}</span>
            <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>button</span> <span style="color:#e2777a">onClick</span><span style="color:#cccccc">=</span><span style="color:#cccccc">{</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
             <span style="color:#f08d49">dispatch</span><span style="color:#cccccc">(</span><span style="color:#cccccc">{</span>type<span style="color:#67cdcc">:</span><span style="color:#7ec699">'increment'</span><span style="color:#cccccc">}</span><span style="color:#cccccc">)</span>
            <span style="color:#cccccc">}</span><span style="color:#cccccc">}</span><span style="color:#cccccc">></span></span>increment<span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span>button</span><span style="color:#cccccc">></span></span>
            <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>button</span> <span style="color:#e2777a">onClick</span><span style="color:#cccccc">=</span><span style="color:#cccccc">{</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
             <span style="color:#f08d49">dispatch</span><span style="color:#cccccc">(</span><span style="color:#cccccc">{</span>type<span style="color:#67cdcc">:</span><span style="color:#7ec699">'decrement'</span><span style="color:#cccccc">}</span><span style="color:#cccccc">)</span>
            <span style="color:#cccccc">}</span><span style="color:#cccccc">}</span><span style="color:#cccccc">></span></span>increment<span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span>button</span><span style="color:#cccccc">></span></span>
        <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>div</span><span style="color:#cccccc">></span></span>
    )
}   
</code></span></span></span>
  1. useRef
  • 获取dom
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">import</span> React<span style="color:#cccccc">,</span><span style="color:#cccccc">{</span>useRef<span style="color:#cccccc">}</span> <span style="color:#cc99cd">from</span> <span style="color:#7ec699">'react'</span>
<span style="color:#cc99cd">const</span> <span style="color:#f08d49">HooksTest</span> <span style="color:#67cdcc">=</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">const</span> inputEl<span style="color:#67cdcc">=</span><span style="color:#f08d49">useRef</span><span style="color:#cccccc">(</span><span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span>
   <span style="color:#cc99cd">function</span> <span style="color:#f08d49">onButtion</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
    <span style="color:#999999">//  inputEl.current 就是我们获取的dom对象</span>
      inputEl<span style="color:#cccccc">.</span>current<span style="color:#cccccc">.</span><span style="color:#f08d49">focus</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> 
   <span style="color:#cccccc">}</span>
  <span style="color:#cc99cd">return</span> <span style="color:#cccccc">(</span>
        <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>div</span><span style="color:#cccccc">></span></span>
            <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>input</span> <span style="color:#e2777a">type</span><span style="color:#7ec699"><span style="color:#cccccc">=</span><span style="color:#cccccc">'</span>text<span style="color:#cccccc">'</span></span> <span style="color:#e2777a">ref</span><span style="color:#cccccc">=</span><span style="color:#cccccc">{</span>inputEl<span style="color:#cccccc">}</span><span style="color:#cccccc">></span></span>
            <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>button</span> <span style="color:#e2777a">onClick</span><span style="color:#cccccc">=</span><span style="color:#cccccc">{</span>onButtion<span style="color:#cccccc">}</span>
            <span style="color:#cccccc">></span></span>Add<span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span>button</span><span style="color:#cccccc">></span></span>
            <span style="color:#cccccc">{</span>count<span style="color:#cccccc">}</span>
        <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>div</span><span style="color:#cccccc">></span></span>
    )
} 

</code></span></span></span>
  • 存变量

因为在函数式组件里没有this来存放一些实例的变量,所以React建议使用useRef来存放有一些会发生变化的值,useRef 不单是为了DOM的ref,同时也是为了存放实例属性

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">const</span> intervalRef<span style="color:#67cdcc">=</span><span style="color:#f08d49">useRef</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span>
<span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
    intervalRef<span style="color:#cccccc">.</span>current<span style="color:#67cdcc">=</span><span style="color:#f08d49">setInterVal</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span><span style="color:#cccccc">}</span><span style="color:#cccccc">)</span>
    <span style="color:#cc99cd">return</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
        <span style="color:#f08d49">clearInterval</span><span style="color:#cccccc">(</span>intervalRef<span style="color:#cccccc">.</span>current<span style="color:#cccccc">)</span>
    <span style="color:#cccccc">}</span>
<span style="color:#cccccc">}</span><span style="color:#cccccc">)</span>
</code></span></span></span>
  1. useImperativeHandle

可以让你在使用ref时自定义暴露给父组件的实例值,useImperativeHandle 应当与forwardRef 一起使用,这样可以父组件可以调用子组件的方法

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#999999">// 父组件</span>
<span style="color:#cc99cd">function</span> <span style="color:#f08d49">Father</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
 <span style="color:#cc99cd">const</span> modelRef <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useRef</span><span style="color:#cccccc">(</span><span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
 <span style="color:#999999">/* 确定 */</span>
  <span style="color:#cc99cd">function</span> <span style="color:#f08d49">sureBtn</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
      <span style="color:#999999">// 调用子组件的方法</span>
    inputRef<span style="color:#cccccc">.</span>current<span style="color:#cccccc">.</span><span style="color:#f08d49">model</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cccccc">}</span>
 <span style="color:#cc99cd">return</span> <span style="color:#cccccc">(</span>
     <span style="color:#67cdcc"><</span><span style="color:#67cdcc">></span>
     <span style="color:#67cdcc"><</span>Button onClick<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>sureBtn<span style="color:#cccccc">}</span><span style="color:#67cdcc">></span>确定<span style="color:#67cdcc"><</span><span style="color:#67cdcc">/</span>Button<span style="color:#67cdcc">></span>
     <span style="color:#67cdcc"><</span>Children ref<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>modelRef<span style="color:#cccccc">}</span><span style="color:#67cdcc">></span><span style="color:#67cdcc"><</span><span style="color:#67cdcc">/</span>Children<span style="color:#67cdcc">></span>
     <span style="color:#67cdcc"><</span><span style="color:#67cdcc">/</span><span style="color:#67cdcc">></span>
 <span style="color:#cccccc">)</span>
<span style="color:#cccccc">}</span>
<span style="color:#999999">// 子组件</span>
<span style="color:#cc99cd">const</span> Children <span style="color:#67cdcc">=</span> React<span style="color:#cccccc">.</span><span style="color:#f08d49">forwardRef</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span>props<span style="color:#cccccc">,</span>ref<span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
<span style="color:#cc99cd">const</span> <span style="color:#cccccc">[</span>visible<span style="color:#cccccc">,</span> setVisible<span style="color:#cccccc">]</span> <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useState</span><span style="color:#cccccc">(</span><span style="color:#f08d49">false</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
    <span style="color:#f08d49">useImperativeHandle</span><span style="color:#cccccc">(</span>ref<span style="color:#cccccc">,</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">(</span><span style="color:#cccccc">{</span>
      <span style="color:#f08d49">model</span><span style="color:#67cdcc">:</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
        <span style="color:#f08d49">setVisible</span><span style="color:#cccccc">(</span><span style="color:#f08d49">true</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
      <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span>
    <span style="color:#cccccc">}</span><span style="color:#cccccc">)</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span><span style="color:#cccccc">)</span>

</code></span></span></span>
  1. useMemo

useMemo的理念和memo差不多,都是根据判断是否满足当前的有限条件来决定是否执行useMemo的callback函数,第二个参数是一个deps数组,数组里的参数变化决定了useMemo是否更新回调函数。

useMemo和useCallback参数一样,区别是useMemo的返回的是缓存的值,useCallback返回的是函数。

  • useMemo减少不必要的渲染
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#999999">// 用 useMemo包裹的list可以限定当且仅当list改变的时候才更新此list,这样就可以避免List重新循环 </span>
 <span style="color:#cccccc">{</span><span style="color:#f08d49">useMemo</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">(</span>
      <span style="color:#67cdcc"><</span>div<span style="color:#67cdcc">></span><span style="color:#cccccc">{</span>
          list<span style="color:#cccccc">.</span><span style="color:#f08d49">map</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span>i<span style="color:#cccccc">,</span> v<span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">(</span>
              <span style="color:#67cdcc"><</span>span
                  key<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>v<span style="color:#cccccc">}</span> <span style="color:#67cdcc">></span>
                  <span style="color:#cccccc">{</span>i<span style="color:#cccccc">.</span>patentName<span style="color:#cccccc">}</span> 
              <span style="color:#67cdcc"><</span><span style="color:#67cdcc">/</span>span<span style="color:#67cdcc">></span>
          <span style="color:#cccccc">)</span><span style="color:#cccccc">)</span><span style="color:#cccccc">}</span>
      <span style="color:#67cdcc"><</span><span style="color:#67cdcc">/</span>div<span style="color:#67cdcc">></span>
<span style="color:#cccccc">)</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">[</span>list<span style="color:#cccccc">]</span><span style="color:#cccccc">)</span><span style="color:#cccccc">}</span>

</code></span></span></span>
  • useMemo减少子组件的渲染次数
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code> <span style="color:#f08d49">useMemo</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">(</span>
     <span style="color:#cccccc">{</span> <span style="color:#999999">/* 减少了PatentTable组件的渲染 */</span> <span style="color:#cccccc">}</span>
        <span style="color:#67cdcc"><</span>PatentTable
            getList<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>getList<span style="color:#cccccc">}</span>
            selectList<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>selectList<span style="color:#cccccc">}</span>
            cacheSelectList<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>cacheSelectList<span style="color:#cccccc">}</span>
            setCacheSelectList<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>setCacheSelectList<span style="color:#cccccc">}</span> <span style="color:#67cdcc">/</span><span style="color:#67cdcc">></span>
 <span style="color:#cccccc">)</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">[</span>listshow<span style="color:#cccccc">,</span> cacheSelectList<span style="color:#cccccc">]</span><span style="color:#cccccc">)</span>
</code></span></span></span>
  • useMemo避免很多不必要的计算开销
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code>
<span style="color:#cc99cd">const</span> <span style="color:#f08d49">Demo</span><span style="color:#67cdcc">=</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
  <span style="color:#999999">/* 用useMemo 包裹之后的log函数可以避免了每次组件更新再重新声明 ,可以限制上下文的执行 */</span>
    <span style="color:#cc99cd">const</span> newLog <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useMemo</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
     <span style="color:#cc99cd">const</span> <span style="color:#f08d49">log</span> <span style="color:#67cdcc">=</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
           <span style="color:#999999">// 大量计算 </span>
           <span style="color:#999999">// 在这里面不能获取实时的其他值</span>
        <span style="color:#cccccc">}</span>
        <span style="color:#cc99cd">return</span> log
    <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span><span style="color:#cccccc">[</span><span style="color:#cccccc">]</span><span style="color:#cccccc">)</span>
    <span style="color:#999999">// or</span>
   <span style="color:#cc99cd">const</span> log2 <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useMemo(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
           <span style="color:#999999">// 大量计算 </span>
        
        <span style="color:#cc99cd">return</span> <span style="color:#999999">// 计算后的值</span>
    <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span><span style="color:#cccccc">[</span>list<span style="color:#cccccc">]</span><span style="color:#cccccc">)</span>
    <span style="color:#cc99cd">return</span> <span style="color:#67cdcc"><</span>div onClick<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#f08d49">newLog</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">}</span> <span style="color:#67cdcc">></span><span style="color:#cccccc">{</span>log2<span style="color:#cccccc">}</span><span style="color:#67cdcc"><</span><span style="color:#67cdcc">/</span>div<span style="color:#67cdcc">></span>
<span style="color:#cccccc">}</span>
</code></span></span></span>
  1. useCallback

useMemo和useCallback接收的参数都是一样,都是依赖项发生变化后才会执行;useMemo返回的是函数运行结果,useCallback返回的是函数;父组件传递一个函数 给子组件的时候,由于函数组件每一次都会生成新的props函数,这就使的每次一个传递给子组件的函数都发生的变化,这样就会触发子组件的更新,有些更新是没有必要的。

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code>
<span style="color:#cc99cd">const</span> <span style="color:#f08d49">Father</span><span style="color:#67cdcc">=</span><span style="color:#cccccc">(</span><span style="color:#cccccc">{</span> id <span style="color:#cccccc">}</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">const</span> getInfo  <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useCallback</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span>sonName<span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
          console<span style="color:#cccccc">.</span><span style="color:#f08d49">log</span><span style="color:#cccccc">(</span>sonName<span style="color:#cccccc">)</span>
    <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span><span style="color:#cccccc">[</span>id<span style="color:#cccccc">]</span><span style="color:#cccccc">)</span>
    <span style="color:#cc99cd">return</span> <span style="color:#67cdcc"><</span>div<span style="color:#67cdcc">></span>
        <span style="color:#cccccc">{</span><span style="color:#999999">/* 点击按钮触发父组件更新 ,但是子组件没有更新 */</span><span style="color:#cccccc">}</span>
        <span style="color:#67cdcc"><</span>button onClick<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#f08d49">setNumber</span><span style="color:#cccccc">(</span>number<span style="color:#67cdcc">+</span><span style="color:#f08d49">1</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">}</span> <span style="color:#67cdcc">></span>增加<span style="color:#67cdcc"><</span><span style="color:#67cdcc">/</span>button<span style="color:#67cdcc">></span>
        <span style="color:#67cdcc"><</span>DemoChildren getInfo<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>getInfo<span style="color:#cccccc">}</span> <span style="color:#67cdcc">/</span><span style="color:#67cdcc">></span>
    <span style="color:#67cdcc"><</span><span style="color:#67cdcc">/</span>div<span style="color:#67cdcc">></span>
<span style="color:#cccccc">}</span>

<span style="color:#999999">/* 用react.memo */</span>
<span style="color:#cc99cd">const</span> Children <span style="color:#67cdcc">=</span> React<span style="color:#cccccc">.</span><span style="color:#f08d49">memo</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span>props<span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>
   <span style="color:#999999">/* 只有初始化的时候打印了 子组件更新 */</span>
    console<span style="color:#cccccc">.</span><span style="color:#f08d49">log</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'子组件更新'</span><span style="color:#cccccc">,</span>props<span style="color:#cccccc">.</span><span style="color:#f08d49">getInfo</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">)</span>
   <span style="color:#cc99cd">return</span> <span style="color:#67cdcc"><</span>div<span style="color:#67cdcc">></span>子组件<span style="color:#67cdcc"><</span><span style="color:#67cdcc">/</span>div<span style="color:#67cdcc">></span>
<span style="color:#cccccc">}</span><span style="color:#cccccc">)</span>

</code></span></span></span>

useCallback必须配合 react.memo pureComponent,否则不但不会提升性能,还有可能降低性能。

react-hooks的诞生,也不是说它能够完全代替class声明的组件,对于业务比较复杂的组件,class组件还是首选,只不过我们可以把class组件内部拆解成funciton组件,根据业务需求,哪些负责逻辑交互,哪些需要动态渲染,然后配合usememo等api,让性能提升起来。react-hooks使用也有一些限制条件,比如说不能放在流程控制语句中,执行上下文也有一定的要求。

#扩展资料

React Hooks 官方文档

useEffect 完整指南

#2 React-hooks原理解析

#2.1 前言

WARNING

阅读以下内容之前先了解一下,hooks出现的动机,同时也要熟悉hooks的用法,可以参考上一篇文章;看完useStateuseEffect源码,我相信你已经基本掌握了hooks;其它的很简单。

废话不多说,我首先克隆一份代码下来

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#f08d49">git</span> clone --branch v17.0.2 https://github.com/facebook/react.git
</code></span></span></span>

hooks导出部分在react/packages/react/src/ReactHooks.js,虽然在react导出,但是真正实现在react-reconciler这个包里面。

前置知识点:

  1. fiber

Fiber是一种数据结构,React使用链表把VirtualDOM节点表示一个Fiber,Fiber是一个执行单元,每次执行完一个执行单元,React会检查现在还剩多少时间,如果没有时间就将控制权让出去,去执行一些高优先级的任务。

  1. 循环链表

  • 是一种链式存储结构,整个链表形成一个环
  • 它的特点是最后一个节点的指针指向头节点

读源码,我们逐个击破的方式:

  1. useState

  2. useEffect

  3. useRef

  4. useCallback

  5. useMemo

hooks不是一个新api也不是一个黑魔法,就是单纯的一个数组,看下面的例子hooks api返回一个数组,一个是当前值,一个是设置当前值的函数。

#hooks中的useState
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">import</span> React <span style="color:#cccccc">,</span><span style="color:#cccccc">{</span>useState<span style="color:#cccccc">}</span><span style="color:#cc99cd">from</span> <span style="color:#7ec699">'react'</span><span style="color:#cccccc">;</span>

<span style="color:#cc99cd">const</span> <span style="color:#f08d49">App</span> <span style="color:#67cdcc">=</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">const</span> <span style="color:#cccccc">[</span>name<span style="color:#cccccc">,</span>setName<span style="color:#cccccc">]</span><span style="color:#67cdcc">=</span><span style="color:#f08d49">useState</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'王艺瑾'</span><span style="color:#cccccc">)</span>
    <span style="color:#cc99cd">return</span> <span style="color:#cccccc">(</span><span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>div</span><span style="color:#cccccc">></span></span>
             <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>div</span><span style="color:#cccccc">></span></span><span style="color:#cccccc">{</span>name<span style="color:#cccccc">}</span><span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span>div</span><span style="color:#cccccc">></span></span>
             <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"><</span>button</span>
                <span style="color:#e2777a">onClick</span><span style="color:#cccccc">=</span><span style="color:#cccccc">{</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span> <span style="color:#f08d49">setName</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'张艺凡'</span><span style="color:#cccccc">)</span><span style="color:#cccccc">}</span>
               <span style="color:#cccccc">></span></span>切换<span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span>button</span><span style="color:#cccccc">></span></span>
           <span style="color:#e2777a"><span style="color:#e2777a"><span style="color:#cccccc"></</span>div</span><span style="color:#cccccc">></span></span>
       <span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
<span style="color:#cc99cd">export</span> <span style="color:#cc99cd">default</span> App<span style="color:#cccccc">;</span>
</code></span></span></span>
  • 上边是一个非常简单的Hook API,创建了name和setName,在页面上展示name,按钮的点击事件修改name

  • 那么在这个过程中setState是如何实现的呢?

#react 包中导出的useState

源码出处:react/packages/react/src/ReactHooks.js

react包中导出的usesate,其实没什么东西,大致看一下就能明白

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">export</span> <span style="color:#cc99cd">function</span> useState<span style="color:#67cdcc"><</span><span style="color:#f8c555">S</span><span style="color:#67cdcc">></span><span style="color:#cccccc">(</span>
  initialState<span style="color:#67cdcc">:</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#f8c555">S</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">|</span> <span style="color:#f8c555">S</span><span style="color:#cccccc">,</span> <span style="color:#999999">// flow类型注解</span>
<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> dispatcher <span style="color:#67cdcc">=</span> <span style="color:#f08d49">resolveDispatcher</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">return</span> dispatcher<span style="color:#cccccc">.</span><span style="color:#f08d49">useState</span><span style="color:#cccccc">(</span>initialState<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>

ReactHooks.js搜索到了useState,函数里先执行了resolveDispatcher,我们先看看resolveDispatcher函数做了写什么? resolveDispatcher函数的执行,获取了ReactCurrentDispatcher的current,那我们在看看ReactCurrentDispatcher是什么?

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">function</span> <span style="color:#f08d49">resolveDispatcher</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> dispatcher <span style="color:#67cdcc">=</span> ReactCurrentDispatcher<span style="color:#cccccc">.</span>current<span style="color:#cccccc">;</span>
  <span style="color:#f08d49">invariant</span><span style="color:#cccccc">(</span>
    dispatcher <span style="color:#67cdcc">!==</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">,</span>
    <span style="color:#7ec699">'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for'</span> <span style="color:#67cdcc">+</span>
      <span style="color:#7ec699">' one of the following reasons:\n'</span> <span style="color:#67cdcc">+</span>
      <span style="color:#7ec699">'1. You might have mismatching versions of React and the renderer (such as React DOM)\n'</span> <span style="color:#67cdcc">+</span>
      <span style="color:#7ec699">'2. You might be breaking the Rules of Hooks\n'</span> <span style="color:#67cdcc">+</span>
      <span style="color:#7ec699">'3. You might have more than one copy of React in the same app\n'</span> <span style="color:#67cdcc">+</span>
      <span style="color:#7ec699">'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.'</span><span style="color:#cccccc">,</span>
  <span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">return</span> dispatcher<span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>

源码出处:react/packages/react/src/ReactCurrentDispatcher.js

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#999999">/**
 * Keeps track of the current dispatcher.
 */</span>
<span style="color:#cc99cd">const</span> ReactCurrentDispatcher <span style="color:#67cdcc">=</span> <span style="color:#cccccc">{</span>
  <span style="color:#999999">/**
   * @internal
   * @type {ReactComponent}
   */</span>
  current<span style="color:#67cdcc">:</span> <span style="color:#cccccc">(</span><span style="color:#cc99cd">null</span><span style="color:#67cdcc">:</span> <span style="color:#cc99cd">null</span> <span style="color:#67cdcc">|</span> Dispatcher<span style="color:#cccccc">)</span><span style="color:#cccccc">,</span>
<span style="color:#cccccc">}</span><span style="color:#cccccc">;</span>

<span style="color:#cc99cd">export</span> <span style="color:#cc99cd">default</span> ReactCurrentDispatcher<span style="color:#cccccc">;</span>
</code></span></span></span>

ReactCurrentDispatcher现在是null,到这里我们线索好像中断了,因为current要有个hooks方法才行;我们可以断点的形式,去看看在mount阶段,react执行了什么?也就是在mount阶段ReactCurrentDispatcher.current挂载的hooks,蓝色部分就是react在初始化阶段执行的函数

下面才是正文,千万不要放弃

源码出处:react/packages/react-reconciler/src/ReactFiberHooks.new.js

renderWithHooks

  • 为什么从renderWithhooks讲起?

因为renderWithhooks是调用函数组件的主要函数,所有的函数组件执行,都会执行这个方法。

下面我说的hooks代表组件中的hooks,例如:useState;hook对象是每次执行hooks所创建的对象

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#999999">// 挂载和更新页面的时候,用的是不同的hooks,hooks在不同的阶段有不同的实现</span>

<span style="color:#999999">/*
  举个例子,页面在初始化阶段我们在页面中调用的useSate实际调用的是mountState,
  在更新阶段调用的是updateState;其他的hooks也是同理
*/</span>

<span style="color:#cc99cd">const</span> HooksDispatcherOnMount <span style="color:#67cdcc">=</span> <span style="color:#cccccc">{</span> <span style="color:#999999">// 存储初次挂载的hook</span>
    useState<span style="color:#67cdcc">:</span> mountState<span style="color:#cccccc">,</span>
    useEffect<span style="color:#67cdcc">:</span>mountEffect
     <span style="color:#67cdcc">...</span><span style="color:#67cdcc">...</span>
<span style="color:#cccccc">}</span>
<span style="color:#cc99cd">const</span> HooksDispatcherOnUpdate <span style="color:#67cdcc">=</span> <span style="color:#cccccc">{</span> <span style="color:#999999">// 存储更新时候的hook</span>
     useState<span style="color:#67cdcc">:</span> updateState<span style="color:#cccccc">,</span>
     useEffect<span style="color:#67cdcc">:</span>updateEffect
     <span style="color:#67cdcc">...</span><span style="color:#67cdcc">...</span>
<span style="color:#cccccc">}</span>

<span style="color:#cc99cd">let</span> currentlyRenderingFiber<span style="color:#cccccc">;</span> <span style="color:#999999">//当前正在使用的fiber</span>
<span style="color:#cc99cd">let</span> workInProgressHook <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">null</span> <span style="color:#999999">// 存储当前最新的hook,跟链表有关系,往下看会明白</span>
<span style="color:#cc99cd">let</span> currentHook<span style="color:#67cdcc">=</span><span style="color:#cc99cd">null</span> <span style="color:#999999">// 在组件更新阶段对应是老的hook</span>

<span style="color:#999999">/**
 * @param {*} current 上一个fiber 初次挂载 的时候null
 * @param {*} workInProgress 这一次正在构建中的fiber树
 * @param {*} Component 当前组件
 */</span>
<span style="color:#cc99cd">export</span> <span style="color:#cc99cd">function</span> <span style="color:#f08d49">renderWithHooks</span><span style="color:#cccccc">(</span>
  current<span style="color:#cccccc">,</span> 
  workInProgress<span style="color:#cccccc">,</span> 
  Component<span style="color:#cccccc">,</span>
  props<span style="color:#cccccc">,</span>
  secondArg<span style="color:#cccccc">,</span>
  <span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>

   <span style="color:#999999">// currentlyRenderingFiber指向本次要构建的fiber(workInProgress)</span>
   <span style="color:#999999">// 要区分一下workInProgress和workInProgressHook,不要搞混了</span>
    currentlyRenderingFiber <span style="color:#67cdcc">=</span> workInProgress<span style="color:#cccccc">;</span> 

   <span style="color:#999999">//在执行组件方法之前,要清空hook链表 因为你肯定要创建新的hook链表,要把新的信息挂载到这2个属性上</span>
   <span style="color:#999999">//在函数组件中 memoizedState以链表的形式存放hook信息,如果在class组件中,memoizedState存放state信息</span>
    workInProgress<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">;</span>
   <span style="color:#999999">// updateQueue存 effect对象,阅读完useEffect源码就会明白</span>
    workInProgress<span style="color:#cccccc">.</span>updateQueue <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">;</span>

    <span style="color:#999999">// current === null || current.memoizedState === null 说明是mount阶段,否则是update阶段</span>
    <span style="color:#999999">// 我们就在这里给ReactCurrentDispatcher.current赋值了</span>
     ReactCurrentDispatcher<span style="color:#cccccc">.</span>current <span style="color:#67cdcc">=</span>
      current <span style="color:#67cdcc">===</span> <span style="color:#cc99cd">null</span> <span style="color:#67cdcc">||</span> current<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">===</span> <span style="color:#cc99cd">null</span>
        <span style="color:#67cdcc">?</span> HooksDispatcherOnMount
        <span style="color:#67cdcc">:</span> HooksDispatcherOnUpdate<span style="color:#cccccc">;</span>

    <span style="color:#999999">// 调用我们的组件函数,然后我们组件里的hooks才会被依次执行</span>
    <span style="color:#cc99cd">let</span> children <span style="color:#67cdcc">=</span> <span style="color:#f08d49">Component</span><span style="color:#cccccc">(</span>props<span style="color:#cccccc">,</span>secondArg<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span> 

   <span style="color:#999999">/*
    我们的hooks必须写在组件函数的内部,当上面组件里的hooks执行完后,
    我们又给ReactCurrentDispatcher.current赋值了,ContextOnlyDispatcher会报错的形式提示,hooks不能函数外面;
    在不同的阶段赋值不同的hooks对象,判断hooks执行是否在函数组件内部
   */</span>
    ReactCurrentDispatcher<span style="color:#cccccc">.</span>current <span style="color:#67cdcc">=</span> ContextOnlyDispatcher<span style="color:#cccccc">;</span>

    currentlyRenderingFiber <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">;</span><span style="color:#999999">//渲染结束 后把currentlyRenderingFiber清空</span>
    workInProgressHook <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">;</span>
    <span style="color:#999999">// 指向当前调度的hooks节点,主要用于update阶段</span>
    currentHook <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">;</span>

    <span style="color:#cc99cd">return</span> children<span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#999999">// 不在函数内写的hooks指向的函数</span>
<span style="color:#cc99cd">const</span> ContextOnlyDispatcher <span style="color:#67cdcc">=</span> <span style="color:#cccccc">{</span>
    useState<span style="color:#67cdcc">:</span>throwInvalidHookError
<span style="color:#cccccc">}</span>
<span style="color:#cc99cd">function</span> <span style="color:#f08d49">throwInvalidHookError</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#f08d49">invariant</span><span style="color:#cccccc">(</span>
    <span style="color:#f08d49">false</span><span style="color:#cccccc">,</span>
    <span style="color:#7ec699">'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for'</span> <span style="color:#67cdcc">+</span>
      <span style="color:#7ec699">' one of the following reasons:\n'</span> <span style="color:#67cdcc">+</span>
      <span style="color:#7ec699">'1. You might have mismatching versions of React and the renderer (such as React DOM)\n'</span> <span style="color:#67cdcc">+</span>
      <span style="color:#7ec699">'2. You might be breaking the Rules of Hooks\n'</span> <span style="color:#67cdcc">+</span>
      <span style="color:#7ec699">'3. You might have more than one copy of React in the same app\n'</span> <span style="color:#67cdcc">+</span>
      <span style="color:#7ec699">'See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.'</span><span style="color:#cccccc">,</span>
  <span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>

renderWithHooks主要做的事情:

  1. 判断是mount阶段还是update阶段给ReactCurrentDispatcher.current赋值。
  2. 执行组件函数,执行hooks。
  3. 清空在执行hooks所赋值的全局对象,下一次更新函数需要再次用到。
  • 有几个memoizedState,需要注意:

    1. currentlyRenderingFiber.memoizedState 是存整个链表,就是每次执行hooks就会创建hook对象,多个hooks所形成的链表。
    2. hook.memoizedState 用于存当前执行的hooks的一些信息。
  • workInProgress和workInProgressHook:

    1. workInProgress 正在构建的fiber
    2. workInProgressHook 正在构建的hook对象
  • currentHook和workInProgressHook

    1. currentHook主要用于更新阶段,在mount阶段创建了hook对象,在更新阶段我们需要取出来,需要复用上一次存的信息,currentHook就是正在执行的这个hooks上一次存的信息。
    2. workInProgressHook正在创建的hook对象,在mount和update阶段都会创建。
  • current:初始化阶段为null,当第一次渲染之后会产生一个fiber树,最终会换成真实的dom树

  • workInProgress:正在构建的fiber树,更新过程中会从current赋值给workInProgress,更新完毕后将当前的 workInProgress树赋值给current。

#2.2 useState

# mount阶段 重要
#1. mountState

初次挂载的时候,useState对应的函数是mountState

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">function</span> <span style="color:#f08d49">basicStateReducer</span><span style="color:#cccccc">(</span>state<span style="color:#cccccc">,</span> action<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">return</span> <span style="color:#cc99cd">typeof</span> action <span style="color:#67cdcc">===</span> <span style="color:#7ec699">'function'</span> <span style="color:#67cdcc">?</span> <span style="color:#f08d49">action</span><span style="color:#cccccc">(</span>state<span style="color:#cccccc">)</span> <span style="color:#67cdcc">:</span> action<span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
<span style="color:#cc99cd">function</span> <span style="color:#f08d49">mountState</span><span style="color:#cccccc">(</span>
  initialState
<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  
  <span style="color:#999999">// 返回当前正在运行的hook对象,构建hook单项链表,下面会详细讲解</span>
  <span style="color:#cc99cd">const</span> hook <span style="color:#67cdcc">=</span> <span style="color:#f08d49">mountWorkInProgressHook</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
    <span style="color:#999999">/*
     初始值如果是函数,就执行函数拿到初始值
     useState((preState)=> return '初始值')
    */</span>
  <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span><span style="color:#cc99cd">typeof</span> initialState <span style="color:#67cdcc">===</span> <span style="color:#7ec699">'function'</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
    initialState <span style="color:#67cdcc">=</span> <span style="color:#f08d49">initialState</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cccccc">}</span>
<span style="color:#999999">// 把初始值赋值给 hook.baseState和hook.memoizedState</span>
  hook<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">=</span> hook<span style="color:#cccccc">.</span>baseState <span style="color:#67cdcc">=</span> initialState<span style="color:#cccccc">;</span>
 <span style="color:#999999">// 定义一个队列</span>
  <span style="color:#cc99cd">const</span> queue <span style="color:#67cdcc">=</span> <span style="color:#cccccc">(</span>hook<span style="color:#cccccc">.</span>queue <span style="color:#67cdcc">=</span> <span style="color:#cccccc">{</span>
    pending<span style="color:#67cdcc">:</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">,</span> <span style="color:#999999">// 存放update对象</span>
    dispatch<span style="color:#67cdcc">:</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">,</span>  <span style="color:#999999">// 放hooks更新函数</span>
    lastRenderedReducer<span style="color:#67cdcc">:</span> basicStateReducer<span style="color:#cccccc">,</span> <span style="color:#999999">//它是一个函数, 用于得到最新的 state</span>
    lastRenderedState<span style="color:#67cdcc">:</span> initialState<span style="color:#cccccc">,</span>  <span style="color:#999999">// 最后一次得到的 state</span>
  <span style="color:#cccccc">}</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>

<span style="color:#999999">/*  
  dispatchAction 是负责更新的函数,就是代表下面的setState函数
  const [state,setState]=useState()
*/</span>
  <span style="color:#cc99cd">const</span> dispatch <span style="color:#67cdcc">=</span> <span style="color:#cccccc">(</span>queue<span style="color:#cccccc">.</span>dispatch <span style="color:#67cdcc">=</span> <span style="color:#cccccc">(</span><span style="color:#f08d49">dispatchAction</span><span style="color:#cccccc">.</span><span style="color:#f08d49">bind</span><span style="color:#cccccc">(</span>
    <span style="color:#cc99cd">null</span><span style="color:#cccccc">,</span>
    currentlyRenderingFiber<span style="color:#cccccc">,</span>
    queue<span style="color:#cccccc">,</span>
  <span style="color:#cccccc">)</span><span style="color:#cccccc">)</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>

 <span style="color:#999999">//  2个值以数值的形式返回</span>
  <span style="color:#cc99cd">return</span> <span style="color:#cccccc">[</span>hook<span style="color:#cccccc">.</span>memoizedState<span style="color:#cccccc">,</span> dispatch<span style="color:#cccccc">]</span><span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>

mountState主要做的事情:

  1. 创建hook对象,在上面存上hooks信息,下次更新的时候可以从对象上获取。
  2. 返回一个数组,包括初始化的值和更新函数
#2. mountWorkInProgressHook

构建hooks单向链表,将组件中的hooks函数以链表的形式串连起来,并赋值给workInProgress的memoizedState;

例子:

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">function</span> <span style="color:#f08d49">work</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> <span style="color:#cccccc">[</span>name<span style="color:#cccccc">,</span>setName<span style="color:#cccccc">]</span><span style="color:#67cdcc">=</span><span style="color:#f08d49">useState</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'h'</span><span style="color:#cccccc">)</span> <span style="color:#999999">// hooks1</span>
  <span style="color:#cc99cd">const</span> age<span style="color:#67cdcc">=</span><span style="color:#f08d49">useRef</span><span style="color:#cccccc">(</span><span style="color:#f08d49">20</span><span style="color:#cccccc">)</span> <span style="color:#999999">// hooks2</span>
   <span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>

   <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span><span style="color:#cccccc">[</span><span style="color:#cccccc">]</span><span style="color:#cccccc">)</span> <span style="color:#999999">// hooks3</span>
<span style="color:#cccccc">}</span>
 <span style="color:#999999">// 构建单向链表</span>
 currentlyRenderingFiber<span style="color:#cccccc">.</span>memoizedState<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>
   memoizedState<span style="color:#67cdcc">:</span><span style="color:#7ec699">'h'</span><span style="color:#cccccc">,</span>
   next<span style="color:#67cdcc">:</span><span style="color:#cccccc">{</span>
      memoizedState<span style="color:#67cdcc">:</span><span style="color:#7ec699">'20'</span><span style="color:#cccccc">,</span>
      next<span style="color:#67cdcc">:</span><span style="color:#cccccc">{</span>
          memoizedState<span style="color:#67cdcc">:</span>effect<span style="color:#cccccc">,</span>
          next<span style="color:#67cdcc">:</span><span style="color:#cc99cd">null</span>
      <span style="color:#cccccc">}</span>
   <span style="color:#cccccc">}</span>
 <span style="color:#cccccc">}</span>
<span style="color:#999999">// hooks1的next指向hooks2,hooks2的next指向hooks3</span>
</code></span></span></span>

为什么构建一个单向链表?

因为我们在组件更新阶段,需要拿到上次的值,拿到上次的值与本次设置的值做对比来判断是否更新

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">function</span> <span style="color:#f08d49">mountWorkInProgressHook</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#999999">//创建一个hooks对象</span>
  <span style="color:#cc99cd">const</span> hook  <span style="color:#67cdcc">=</span> <span style="color:#cccccc">{</span> 
    memoizedState<span style="color:#67cdcc">:</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">,</span> <span style="color:#999999">// useState中保存state信息,useEffect中保存Effect对象,useMemo中保存缓存的值和依赖;useRef保存的是ref对象</span>
    baseState<span style="color:#67cdcc">:</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">,</span> <span style="color:#999999">// useState和useReducer中保存最新的state</span>
    baseQueue<span style="color:#67cdcc">:</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">,</span><span style="color:#999999">// useState和useReducer中保存最新的更新队列</span>
    queue<span style="color:#67cdcc">:</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">,</span> <span style="color:#999999">// 自己的更新队列,形成环状链表</span>
    next<span style="color:#67cdcc">:</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">,</span> <span style="color:#999999">// 下一个更新,就是我们下的页面中下一个hooks</span>
  <span style="color:#cccccc">}</span><span style="color:#cccccc">;</span>
     
    <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span>workInProgressHook <span style="color:#67cdcc">===</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
      <span style="color:#999999">//说明这是我们的第一个hook</span>
        currentlyRenderingFiber<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">=</span> workInProgressHook <span style="color:#67cdcc">=</span> hook<span style="color:#cccccc">;</span>
    <span style="color:#cccccc">}</span> <span style="color:#cc99cd">else</span> <span style="color:#cccccc">{</span>
       <span style="color:#999999">// 说明函数组件中不止一个hooks</span>
        workInProgressHook <span style="color:#67cdcc">=</span> workInProgressHook<span style="color:#cccccc">.</span>next <span style="color:#67cdcc">=</span> hook<span style="color:#cccccc">;</span>
    <span style="color:#cccccc">}</span>
    <span style="color:#cc99cd">return</span> workInProgressHook<span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>

如果上面构建hooks单向链表没有看懂,请看下面解析

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code>   <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span>workInProgressHook <span style="color:#67cdcc">===</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
      <span style="color:#999999">//说明这是我们的第一个hook</span>
        currentlyRenderingFiber<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">=</span> workInProgressHook <span style="color:#67cdcc">=</span> hook<span style="color:#cccccc">;</span>
    <span style="color:#cccccc">}</span> <span style="color:#cc99cd">else</span> <span style="color:#cccccc">{</span>
      <span style="color:#999999">// 说明函数组件中不止一个hooks</span>
        workInProgressHook <span style="color:#67cdcc">=</span> workInProgressHook<span style="color:#cccccc">.</span>next <span style="color:#67cdcc">=</span> hook<span style="color:#cccccc">;</span>
    <span style="color:#cccccc">}</span>

</code></span></span></span>
  1. 第一次我们创建了hook对象,在堆内存中开辟了一块空间, currentlyRenderingFiber.memoizedStateworkInProgressHook都指向了这个值,对象是引用类型值;我们称这个值为hooks1吧。

currentlyRenderingFiber.memoizedState = hooks1

  1. 第二次我们再次创建了hook对象,在堆内存中又开辟了一块空间,我们称这个值为hooks2吧,workInProgressHook.next指向了hooks2,也就是hooks1.next指向了hook2;因为当前的workInProgressHook和hooks1指向同一个地址,只要有一个修改内存里的值,其他变量只要引用该值了,也会随之发生变化;最后又把hooks2又赋值给workInProgressHook,那么workInProgressHook又指向了hooks2。

hooks1.next= hooks2

workInProgressHook=hooks2

  1. 第三次我们再次创建了hook对象,在堆内存中又开辟了一块空间,我们称这个值为hooks3吧,hooks3又赋值给了workInProgressHook.next,现在的workInProgressHook和hooks2指向是同一个地址,那么我改变workInProgressHook.next就是改变hooks2的next。

hooks2.next= hooks3

workInProgressHook=hooks3

workInProgressHook始终和最新hook对象指向同一个地址,这样就方便修改上一个hook对象的next

#3. dispatchAction
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#999999">/**
 * @param {*} fiber 当前正在使用的fiber
 * @param {*} queue 队列的初始对象
 * @param {*} action 更新函数或者要更新的值
 * 
 */</span>
<span style="color:#cc99cd">function</span> <span style="color:#f08d49">dispatchAction</span><span style="color:#cccccc">(</span>fiber<span style="color:#cccccc">,</span> queue<span style="color:#cccccc">,</span> action<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#999999">// 创建一个update对象</span>
 <span style="color:#cc99cd">const</span> update<span style="color:#67cdcc">=</span> <span style="color:#cccccc">{</span>
    action<span style="color:#cccccc">,</span>
    eagerReducer<span style="color:#67cdcc">:</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">,</span>
    eagerState<span style="color:#67cdcc">:</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">,</span>
    next<span style="color:#67cdcc">:</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">,</span>
  <span style="color:#cccccc">}</span>
  <span style="color:#cc99cd">const</span> pending <span style="color:#67cdcc">=</span> queue<span style="color:#cccccc">.</span>pending<span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span>pending <span style="color:#67cdcc">===</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>  <span style="color:#999999">// 证明第一次更新</span>
    update<span style="color:#cccccc">.</span>next <span style="color:#67cdcc">=</span> update<span style="color:#cccccc">;</span><span style="color:#999999">//让自己和自己构建成一个环状链表</span>
  <span style="color:#cccccc">}</span> <span style="color:#cc99cd">else</span> <span style="color:#cccccc">{</span> <span style="color:#999999">// 不是第一次更新</span>
    update<span style="color:#cccccc">.</span>next <span style="color:#67cdcc">=</span> pending<span style="color:#cccccc">.</span>next<span style="color:#cccccc">;</span>
    pending<span style="color:#cccccc">.</span>next <span style="color:#67cdcc">=</span> update<span style="color:#cccccc">;</span>
  <span style="color:#cccccc">}</span>
  queue<span style="color:#cccccc">.</span>pending <span style="color:#67cdcc">=</span> update<span style="color:#cccccc">;</span>
<span style="color:#999999">// queue.pending`永远指向最后一个更新,pending.next 永远指向第一个更新</span>
  <span style="color:#cc99cd">const</span> currentState <span style="color:#67cdcc">=</span> queue<span style="color:#cccccc">.</span>lastRenderedState<span style="color:#cccccc">;</span><span style="color:#999999">// 上一次的state</span>
  <span style="color:#cc99cd">const</span> eagerState <span style="color:#67cdcc">=</span> <span style="color:#f08d49">lastRenderedReducer</span><span style="color:#cccccc">(</span>currentState<span style="color:#cccccc">,</span> action<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span><span style="color:#999999">//获取最新的state</span>

  update<span style="color:#cccccc">.</span>eagerState <span style="color:#67cdcc">=</span> eagerState<span style="color:#cccccc">;</span> 
  <span style="color:#999999">// 判断上一次的值和当前的值是否一样,是同一个值或同一个引用就return,不进行更新</span>
  <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span><span style="color:#f08d49">is</span><span style="color:#cccccc">(</span>eagerState<span style="color:#cccccc">,</span> currentState<span style="color:#cccccc">)</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span> 
      <span style="color:#cc99cd">return</span>
    <span style="color:#cccccc">}</span>
    <span style="color:#999999">// 调度渲染当前fiber,scheduleUpdateOnFiber是react渲染更新的主要函数。</span>
  <span style="color:#f08d49">scheduleUpdateOnFiber</span><span style="color:#cccccc">(</span>fiber<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>

类组件更新调用setState,函数组件hooks更新调用dispatchAction,都会产生一个update对象,里面记录此处更新的信息; 把update对象放在queue.pending上。

为什么创建update对象?

每次创建update对象,是希望形成一个环状链表。我们看下面一个例子,三次setCount的update对象会暂时放在queue.pending上,组件里的state不会立即更新,在下一次函数组件执行的时候,三次update会被合并到baseQueue上,我们要获取最新的状态,会一次执行update上的每一个action,得到最新的state。

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">function</span> <span style="color:#f08d49">work</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> <span style="color:#cccccc">[</span>count<span style="color:#cccccc">,</span>setCount<span style="color:#cccccc">]</span><span style="color:#67cdcc">=</span><span style="color:#f08d49">useState</span><span style="color:#cccccc">(</span><span style="color:#f08d49">0</span><span style="color:#cccccc">)</span> 
  <span style="color:#cc99cd">function</span> <span style="color:#f08d49">add</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
    <span style="color:#f08d49">setCount</span><span style="color:#cccccc">(</span><span style="color:#f08d49">1</span><span style="color:#cccccc">)</span>
    <span style="color:#f08d49">setCount</span><span style="color:#cccccc">(</span><span style="color:#f08d49">2</span><span style="color:#cccccc">)</span>
    <span style="color:#f08d49">setCount</span><span style="color:#cccccc">(</span><span style="color:#f08d49">3</span><span style="color:#cccccc">)</span>
  <span style="color:#cccccc">}</span>
  <span style="color:#cc99cd">return</span> <span style="color:#cccccc">(</span>
    <span style="color:#67cdcc"><</span>button onClick<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>add<span style="color:#cccccc">}</span><span style="color:#67cdcc">></span><span style="color:#67cdcc"><</span><span style="color:#67cdcc">/</span>button<span style="color:#67cdcc">></span>
  <span style="color:#cccccc">)</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>

为什么不是直接执行最后一个setCount?

如果setCount((state)=>{state+1})参数是函数,那么需要依赖state,下一个要依赖上一个的state;所以需要都执行一遍才能 拿到准确的值。

# update阶段 重要
#1.updateState
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">function</span> <span style="color:#f08d49">basicStateReducer</span><span style="color:#cccccc">(</span>state<span style="color:#cccccc">,</span> action<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#999999">// $FlowFixMe: Flow doesn't like mixed types</span>
  <span style="color:#cc99cd">return</span> <span style="color:#cc99cd">typeof</span> action <span style="color:#67cdcc">===</span> <span style="color:#7ec699">'function'</span> <span style="color:#67cdcc">?</span> <span style="color:#f08d49">action</span><span style="color:#cccccc">(</span>state<span style="color:#cccccc">)</span> <span style="color:#67cdcc">:</span> action<span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>

<span style="color:#999999">// 可以看出updateState其实调用的是updateReducer</span>
<span style="color:#cc99cd">function</span> <span style="color:#f08d49">updateState</span><span style="color:#cccccc">(</span>
  initialState
<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">return</span> <span style="color:#f08d49">updateReducer</span><span style="color:#cccccc">(</span>basicStateReducer<span style="color:#cccccc">,</span> initialState<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>

<span style="color:#cc99cd">function</span> <span style="color:#f08d49">updateReducer</span><span style="color:#cccccc">(</span>reducer<span style="color:#cccccc">,</span> initialArg<span style="color:#cccccc">)</span><span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">let</span> hook <span style="color:#67cdcc">=</span> <span style="color:#f08d49">updateWorkInProgressHook</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span> <span style="color:#999999">// 构建新的链表</span>
    <span style="color:#cc99cd">const</span> queue <span style="color:#67cdcc">=</span> hook<span style="color:#cccccc">.</span>queue<span style="color:#cccccc">;</span><span style="color:#999999">//hooks自己的更新队列</span>

    <span style="color:#999999">// lastRenderedReducer用于得到最新state,它是一个函数</span>
    queue<span style="color:#cccccc">.</span>lastRenderedReducer <span style="color:#67cdcc">=</span> reducer<span style="color:#cccccc">;</span>

    <span style="color:#999999">// currentHook记录了当前这个hooks上一次存在链表上的memoizedState、queue、next等信息</span>
    <span style="color:#cc99cd">const</span> current <span style="color:#67cdcc">=</span> currentHook<span style="color:#cccccc">;</span>

   <span style="color:#999999">// pendingQueue就是更新队列的最后一个update对象</span>
    <span style="color:#cc99cd">const</span> pendingQueue  <span style="color:#67cdcc">=</span> queue<span style="color:#cccccc">.</span>pending<span style="color:#cccccc">;</span>

    <span style="color:#cc99cd">if</span><span style="color:#cccccc">(</span>pendingQueue<span style="color:#67cdcc">!==</span><span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span><span style="color:#cccccc">{</span>
      
        <span style="color:#cc99cd">let</span> first <span style="color:#67cdcc">=</span> pendingQueue<span style="color:#cccccc">.</span>next<span style="color:#cccccc">;</span><span style="color:#999999">//第一个更新对象</span>
        <span style="color:#cc99cd">let</span> newState <span style="color:#67cdcc">=</span> current<span style="color:#cccccc">.</span>memoizedState<span style="color:#cccccc">;</span><span style="color:#999999">//拿到老状态</span>
        <span style="color:#cc99cd">let</span> update <span style="color:#67cdcc">=</span> first<span style="color:#cccccc">;</span>
        <span style="color:#cc99cd">do</span><span style="color:#cccccc">{</span>
            <span style="color:#cc99cd">const</span> action <span style="color:#67cdcc">=</span> update<span style="color:#cccccc">.</span>action<span style="color:#cccccc">;</span><span style="color:#999999">//action:就是传的参数,例如setState('参数')</span>
            newState <span style="color:#67cdcc">=</span> <span style="color:#f08d49">reducer</span><span style="color:#cccccc">(</span>newState<span style="color:#cccccc">,</span>action<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span><span style="color:#999999">//计算新状态,因为如果传的是函数,要依赖老状态</span>
            update <span style="color:#67cdcc">=</span> update<span style="color:#cccccc">.</span>next<span style="color:#cccccc">;</span>
        <span style="color:#cccccc">}</span><span style="color:#cc99cd">while</span><span style="color:#cccccc">(</span>update <span style="color:#67cdcc">!==</span> <span style="color:#cc99cd">null</span> <span style="color:#67cdcc">&&</span> update <span style="color:#67cdcc">!==</span> first<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>

        queue<span style="color:#cccccc">.</span>pending <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">;</span><span style="color:#999999">//更新过了可以清空更新环形链表</span>
        hook<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">=</span>  newState<span style="color:#cccccc">;</span><span style="color:#999999">//让新的hook对象的memoizedState等于计算的新状态    </span>
        queue<span style="color:#cccccc">.</span>lastRenderedState <span style="color:#67cdcc">=</span> newState<span style="color:#cccccc">;</span><span style="color:#999999">//把新状态也赋值给lastRenderedState一份</span>
    <span style="color:#cccccc">}</span>
    <span style="color:#cc99cd">const</span> dispatch <span style="color:#67cdcc">=</span> <span style="color:#f08d49">dispatchAction</span><span style="color:#cccccc">.</span><span style="color:#f08d49">bind</span><span style="color:#cccccc">(</span><span style="color:#cc99cd">null</span><span style="color:#cccccc">,</span> currentlyRenderingFiber<span style="color:#cccccc">,</span> queue<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
    <span style="color:#cc99cd">return</span> <span style="color:#cccccc">[</span>hook<span style="color:#cccccc">.</span>memoizedState<span style="color:#cccccc">,</span> dispatch<span style="color:#cccccc">]</span><span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>

</code></span></span></span>
#2. updateWorkInProgressHook
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">function</span> <span style="color:#f08d49">updateWorkInProgressHook</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">{</span>

    <span style="color:#cc99cd">let</span> nextCurrentHook<span style="color:#cccccc">;</span>
   <span style="color:#999999">//currentHook为null,说明执行的是第一个hooks;currentHook就是老的hook对象</span>
    <span style="color:#cc99cd">if</span><span style="color:#cccccc">(</span>currentHook <span style="color:#67cdcc">===</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span><span style="color:#cccccc">{</span>
       <span style="color:#999999">// current:老的fiber、workInProgress:正在构建的fiber</span>
      <span style="color:#cc99cd">let</span> current <span style="color:#67cdcc">=</span> currentlyRenderingFiber<span style="color:#cccccc">.</span>alternate<span style="color:#cccccc">;</span><span style="color:#999999">//alternate属性 对应的是老的fiBer</span>
      <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span>current <span style="color:#67cdcc">!==</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
        <span style="color:#999999">// 老的fiber的memoizedState对应的是链表的第一个节点</span>
        nextCurrentHook <span style="color:#67cdcc">=</span> current<span style="color:#cccccc">.</span>memoizedState<span style="color:#cccccc">;</span>
      <span style="color:#cccccc">}</span> <span style="color:#cc99cd">else</span> <span style="color:#cccccc">{</span>
        nextCurrentHook <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">;</span>
      <span style="color:#cccccc">}</span>
    <span style="color:#cccccc">}</span><span style="color:#cc99cd">else</span><span style="color:#cccccc">{</span>
      <span style="color:#999999">// 不是第一个hooks,那么指向下一个 hooks</span>
        nextCurrentHook<span style="color:#67cdcc">=</span>currentHook<span style="color:#cccccc">.</span>next<span style="color:#cccccc">;</span>
    <span style="color:#cccccc">}</span>

    currentHook<span style="color:#67cdcc">=</span>nextCurrentHook<span style="color:#cccccc">;</span>

    <span style="color:#999999">//创建新的hook对象</span>
    <span style="color:#cc99cd">const</span> newHook <span style="color:#67cdcc">=</span> <span style="color:#cccccc">{</span>
        memoizedState<span style="color:#67cdcc">:</span>currentHook<span style="color:#cccccc">.</span>memoizedState<span style="color:#cccccc">,</span>
        queue<span style="color:#67cdcc">:</span>currentHook<span style="color:#cccccc">.</span>queue<span style="color:#cccccc">,</span>
        next<span style="color:#67cdcc">:</span><span style="color:#cc99cd">null</span>
    <span style="color:#cccccc">}</span>

<span style="color:#999999">// 创建新链表</span>
    <span style="color:#cc99cd">if</span><span style="color:#cccccc">(</span>workInProgressHook <span style="color:#67cdcc">===</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span><span style="color:#cccccc">{</span>
        currentlyRenderingFiber<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">=</span> workInProgressHook <span style="color:#67cdcc">=</span> newHook<span style="color:#cccccc">;</span>
    <span style="color:#cccccc">}</span><span style="color:#cc99cd">else</span><span style="color:#cccccc">{</span>
       workInProgressHook <span style="color:#67cdcc">=</span> workInProgressHook<span style="color:#cccccc">.</span>next <span style="color:#67cdcc">=</span> newHook<span style="color:#cccccc">;</span>
    <span style="color:#cccccc">}</span>

    <span style="color:#cc99cd">return</span> workInProgressHook<span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>

#2.3 useEffect

# mount阶段 重要
#1. mountEffect
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#999999">/**
 * @param {function} create - 回调函数
 * @param {Array} deps - 依赖数组
 * 
*/</span>

 <span style="color:#cc99cd">const</span> PassiveEffect <span style="color:#67cdcc">=</span> <span style="color:#f08d49">0b000000001000000000</span><span style="color:#cccccc">;</span> <span style="color:#999999">// useEffect</span>
 <span style="color:#cc99cd">const</span> PassiveStaticEffect <span style="color:#67cdcc">=</span> <span style="color:#f08d49">0b001000000000000000</span><span style="color:#cccccc">;</span>

<span style="color:#cc99cd">function</span> <span style="color:#f08d49">mountEffect</span><span style="color:#cccccc">(</span>
  create<span style="color:#cccccc">,</span>
  deps<span style="color:#cccccc">,</span>
<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#999999">//  如果在代码中看见 __DEV__,可以不用关心,开发环境才会执行里面的代码,生产会tree shaking</span>
  <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span>__DEV__<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span><span style="color:#cccccc">}</span> 

    <span style="color:#cc99cd">return</span> <span style="color:#f08d49">mountEffectImpl</span><span style="color:#cccccc">(</span>
      PassiveEffect <span style="color:#67cdcc">|</span> PassiveStaticEffect<span style="color:#cccccc">,</span> <span style="color:#999999">// 按位操作</span>
      HookPassive<span style="color:#cccccc">,</span>
      create<span style="color:#cccccc">,</span>
      deps<span style="color:#cccccc">,</span>
    <span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  
<span style="color:#cccccc">}</span>
</code></span></span></span>
#2. mountEffectImpl
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code>
<span style="color:#999999">// 位操作 :| 、&</span>

<span style="color:#cc99cd">const</span> HookHasEffect<span style="color:#67cdcc">=</span> <span style="color:#f08d49">0b001</span><span style="color:#cccccc">;</span>
 hookFlags <span style="color:#67cdcc">=</span> <span style="color:#f08d49">0b100</span><span style="color:#cccccc">;</span>

<span style="color:#cc99cd">function</span> <span style="color:#f08d49">mountEffectImpl</span><span style="color:#cccccc">(</span>fiberFlags<span style="color:#cccccc">,</span> hookFlags<span style="color:#cccccc">,</span> create<span style="color:#cccccc">,</span> deps<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> hook <span style="color:#67cdcc">=</span> <span style="color:#f08d49">mountWorkInProgressHook</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span> <span style="color:#999999">// 构建单向链表</span>
  <span style="color:#cc99cd">const</span> nextDeps <span style="color:#67cdcc">=</span> deps <span style="color:#67cdcc">===</span> <span style="color:#cc99cd">undefined</span> <span style="color:#67cdcc">?</span> <span style="color:#cc99cd">null</span> <span style="color:#67cdcc">:</span> deps<span style="color:#cccccc">;</span>
  currentlyRenderingFiber<span style="color:#cccccc">.</span>flags <span style="color:#67cdcc">|=</span> fiberFlags<span style="color:#cccccc">;</span>
  <span style="color:#999999">/*
    每个hooks都会创建个hook对象,memoizedState在useState中保存的是state
    在useEffect中保存的effect对象
  */</span>
  hook<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">=</span> <span style="color:#f08d49">pushEffect</span><span style="color:#cccccc">(</span>
    HookHasEffect <span style="color:#67cdcc">|</span> hookFlags<span style="color:#cccccc">,</span>
    create<span style="color:#cccccc">,</span>
    <span style="color:#cc99cd">undefined</span><span style="color:#cccccc">,</span>
    nextDeps<span style="color:#cccccc">,</span>
  <span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>
#3. pushEffect

pushEffect 创建effec对象,并形成环状链表存值与updateQueue上

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code>
<span style="color:#cc99cd">function</span> <span style="color:#f08d49">createFunctionComponentUpdateQueue</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">return</span> <span style="color:#cccccc">{</span>
    lastEffect<span style="color:#67cdcc">:</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">,</span>
  <span style="color:#cccccc">}</span><span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
<span style="color:#cc99cd">function</span> <span style="color:#f08d49">pushEffect</span><span style="color:#cccccc">(</span>tag<span style="color:#cccccc">,</span> create<span style="color:#cccccc">,</span> destroy<span style="color:#cccccc">,</span> deps<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>

  <span style="color:#999999">// 创建effect对象</span>
  <span style="color:#cc99cd">const</span> effect <span style="color:#67cdcc">=</span> <span style="color:#cccccc">{</span>
    tag<span style="color:#cccccc">,</span>
    create<span style="color:#cccccc">,</span>
    destroy<span style="color:#cccccc">,</span>
    deps<span style="color:#cccccc">,</span>
    next<span style="color:#67cdcc">:</span><span style="color:#cc99cd">null</span>
  <span style="color:#cccccc">}</span><span style="color:#cccccc">;</span>

  <span style="color:#cc99cd">let</span> componentUpdateQueue <span style="color:#67cdcc">=</span> currentlyRenderingFiber<span style="color:#cccccc">.</span>updateQueue<span style="color:#cccccc">;</span>
  <span style="color:#999999">// 第一个useEffect</span>
  <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span>componentUpdateQueue <span style="color:#67cdcc">===</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#999999">// componentUpdateQueue : {lastEffect:null}</span>
    componentUpdateQueue <span style="color:#67cdcc">=</span> <span style="color:#f08d49">createFunctionComponentUpdateQueue</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>

    currentlyRenderingFiber<span style="color:#cccccc">.</span>updateQueue <span style="color:#67cdcc">=</span>  componentUpdateQueue
      <span style="color:#999999">// effect 赋值给effect.next;它们指向了内存中同一个地址</span>
      <span style="color:#999999">// componentUpdateQueue.lastEffect指向effect 也就是componentUpdateQueue.updateQueue.lastEffect指向了 Effect</span>
    componentUpdateQueue<span style="color:#cccccc">.</span>lastEffect <span style="color:#67cdcc">=</span> effect<span style="color:#cccccc">.</span>next <span style="color:#67cdcc">=</span> effect<span style="color:#cccccc">;</span>
  <span style="color:#cccccc">}</span> <span style="color:#cc99cd">else</span> <span style="color:#cccccc">{</span> <span style="color:#999999">// 存在多个useEffect</span>
    
    <span style="color:#999999">// componentUpdateQueue.lastEffect 就是上一个Effect对象</span>
      <span style="color:#cc99cd">const</span> lastEffect <span style="color:#67cdcc">=</span> componentUpdateQueue<span style="color:#cccccc">.</span>lastEffect<span style="color:#cccccc">;</span> 
      <span style="color:#cc99cd">const</span> firstEffect <span style="color:#67cdcc">=</span> lastEffect<span style="color:#cccccc">.</span>next<span style="color:#cccccc">;</span> 
      lastEffect<span style="color:#cccccc">.</span>next <span style="color:#67cdcc">=</span> effect<span style="color:#cccccc">;</span>
      effect<span style="color:#cccccc">.</span>next <span style="color:#67cdcc">=</span> firstEffect<span style="color:#cccccc">;</span>
      componentUpdateQueue<span style="color:#cccccc">.</span>lastEffect <span style="color:#67cdcc">=</span> effect<span style="color:#cccccc">;</span>
    <span style="color:#cccccc">}</span>

  <span style="color:#cccccc">}</span>
  <span style="color:#cc99cd">return</span> effect<span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
<span style="color:#999999">// componentUpdateQueue.lastEffect 永远指向最新的</span>
</code></span></span></span>

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>consoe<span style="color:#cccccc">.</span><span style="color:#f08d49">log</span><span style="color:#cccccc">(</span><span style="color:#f08d49">1</span><span style="color:#cccccc">)</span><span style="color:#cccccc">}</span><span style="color:#cccccc">,</span><span style="color:#cccccc">[</span><span style="color:#cccccc">]</span><span style="color:#cccccc">)</span>
<span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>consoe<span style="color:#cccccc">.</span><span style="color:#f08d49">log</span><span style="color:#cccccc">(</span><span style="color:#f08d49">2</span><span style="color:#cccccc">)</span><span style="color:#cccccc">}</span><span style="color:#cccccc">,</span><span style="color:#cccccc">[</span><span style="color:#cccccc">]</span><span style="color:#cccccc">)</span>
<span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>consoe<span style="color:#cccccc">.</span><span style="color:#f08d49">log</span><span style="color:#cccccc">(</span><span style="color:#f08d49">3</span><span style="color:#cccccc">)</span><span style="color:#cccccc">}</span><span style="color:#cccccc">,</span><span style="color:#cccccc">[</span><span style="color:#cccccc">]</span><span style="color:#cccccc">)</span>
<span style="color:#999999">// 执行第一个effect</span>
<span style="color:#cc99cd">const</span> effect1<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>
  <span style="color:#f08d49">create</span><span style="color:#67cdcc">:</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>consoe<span style="color:#cccccc">.</span><span style="color:#f08d49">log</span><span style="color:#cccccc">(</span><span style="color:#f08d49">1</span><span style="color:#cccccc">)</span><span style="color:#cccccc">}</span><span style="color:#cccccc">,</span>
  deps<span style="color:#67cdcc">:</span><span style="color:#cccccc">[</span><span style="color:#cccccc">]</span>
  next<span style="color:#67cdcc">:</span>effect1
<span style="color:#cccccc">}</span>


<span style="color:#999999">// 执行第二个effect</span>
<span style="color:#cc99cd">const</span> effect1<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>
  <span style="color:#f08d49">create</span><span style="color:#67cdcc">:</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>consoe<span style="color:#cccccc">.</span><span style="color:#f08d49">log</span><span style="color:#cccccc">(</span><span style="color:#f08d49">1</span><span style="color:#cccccc">)</span><span style="color:#cccccc">}</span><span style="color:#cccccc">,</span>
  deps<span style="color:#67cdcc">:</span><span style="color:#cccccc">[</span><span style="color:#cccccc">]</span>
  next<span style="color:#67cdcc">:</span>effect2
<span style="color:#cccccc">}</span>

<span style="color:#cc99cd">const</span> effect2<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>
  <span style="color:#f08d49">create</span><span style="color:#67cdcc">:</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>consoe<span style="color:#cccccc">.</span><span style="color:#f08d49">log</span><span style="color:#cccccc">(</span><span style="color:#f08d49">1</span><span style="color:#cccccc">)</span><span style="color:#cccccc">}</span><span style="color:#cccccc">,</span>
  deps<span style="color:#67cdcc">:</span><span style="color:#cccccc">[</span><span style="color:#cccccc">]</span>
  next<span style="color:#67cdcc">:</span>effect1
<span style="color:#cccccc">}</span>

<span style="color:#999999">// 执行第三个effect</span>
<span style="color:#cc99cd">const</span> effect2<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>
  <span style="color:#f08d49">create</span><span style="color:#67cdcc">:</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>consoe<span style="color:#cccccc">.</span><span style="color:#f08d49">log</span><span style="color:#cccccc">(</span><span style="color:#f08d49">1</span><span style="color:#cccccc">)</span><span style="color:#cccccc">,</span>
  deps<span style="color:#67cdcc">:</span><span style="color:#cccccc">[</span><span style="color:#cccccc">]</span>
  next<span style="color:#67cdcc">:</span>effect3
<span style="color:#cccccc">}</span>
<span style="color:#cc99cd">const</span> effect3<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>
  <span style="color:#f08d49">create</span><span style="color:#67cdcc">:</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#cccccc">{</span>consoe<span style="color:#cccccc">.</span><span style="color:#f08d49">log</span><span style="color:#cccccc">(</span><span style="color:#f08d49">1</span><span style="color:#cccccc">)</span><span style="color:#cccccc">,</span>
  deps<span style="color:#67cdcc">:</span><span style="color:#cccccc">[</span><span style="color:#cccccc">]</span>
  next<span style="color:#67cdcc">:</span>effect1  <span style="color:#999999">// effect1指向的是effect2</span>
<span style="color:#cccccc">}</span>

</code></span></span></span>
# update阶段 重要
#1. updateEffect
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">function</span> <span style="color:#f08d49">updateEffect</span><span style="color:#cccccc">(</span>
  create<span style="color:#cccccc">,</span>
  deps<span style="color:#cccccc">,</span>
<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>

  <span style="color:#cc99cd">return</span> <span style="color:#f08d49">updateEffectImpl</span><span style="color:#cccccc">(</span>PassiveEffect<span style="color:#cccccc">,</span> HookPassive<span style="color:#cccccc">,</span> create<span style="color:#cccccc">,</span> deps<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>
#2. updateEffectImpl
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">function</span> <span style="color:#f08d49">areHookInputsEqual</span><span style="color:#cccccc">(</span>
  nextDeps<span style="color:#cccccc">,</span>
  prevDeps<span style="color:#cccccc">,</span>
<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>

  <span style="color:#cc99cd">for</span> <span style="color:#cccccc">(</span><span style="color:#cc99cd">let</span> i <span style="color:#67cdcc">=</span> <span style="color:#f08d49">0</span><span style="color:#cccccc">;</span> i <span style="color:#67cdcc"><</span> prevDeps<span style="color:#cccccc">.</span>length <span style="color:#67cdcc">&&</span> i <span style="color:#67cdcc"><</span> nextDeps<span style="color:#cccccc">.</span>length<span style="color:#cccccc">;</span> i<span style="color:#67cdcc">++</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span><span style="color:#f08d49">is</span><span style="color:#cccccc">(</span>nextDeps<span style="color:#cccccc">[</span>i<span style="color:#cccccc">]</span><span style="color:#cccccc">,</span> prevDeps<span style="color:#cccccc">[</span>i<span style="color:#cccccc">]</span><span style="color:#cccccc">)</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
      <span style="color:#cc99cd">continue</span><span style="color:#cccccc">;</span>
    <span style="color:#cccccc">}</span>
    <span style="color:#cc99cd">return</span> <span style="color:#f08d49">false</span><span style="color:#cccccc">;</span>
  <span style="color:#cccccc">}</span>
  <span style="color:#cc99cd">return</span> <span style="color:#f08d49">true</span><span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>

<span style="color:#cc99cd">function</span> <span style="color:#f08d49">updateEffectImpl</span><span style="color:#cccccc">(</span>fiberFlags<span style="color:#cccccc">,</span> hookFlags<span style="color:#cccccc">,</span> create<span style="color:#cccccc">,</span> deps<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
<span style="color:#999999">// updateWorkInProgressHook可以往上看,就是创建新的hook对象,不过会复用上一次存的一些信息</span>
  <span style="color:#cc99cd">const</span> hook <span style="color:#67cdcc">=</span> <span style="color:#f08d49">updateWorkInProgressHook</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>

  <span style="color:#cc99cd">const</span> nextDeps <span style="color:#67cdcc">=</span> deps <span style="color:#67cdcc">===</span> <span style="color:#cc99cd">undefined</span> <span style="color:#67cdcc">?</span> <span style="color:#cc99cd">null</span> <span style="color:#67cdcc">:</span> deps<span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">let</span> destroy <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">undefined</span><span style="color:#cccccc">;</span>

<span style="color:#999999">// currentHook 可以说是老的hook</span>
  <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span>currentHook <span style="color:#67cdcc">!==</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
    <span style="color:#999999">// 拿到上一次存的effect对象</span>
    <span style="color:#cc99cd">const</span> prevEffect <span style="color:#67cdcc">=</span> currentHook<span style="color:#cccccc">.</span>memoizedState<span style="color:#cccccc">;</span>
    destroy <span style="color:#67cdcc">=</span> prevEffect<span style="color:#cccccc">.</span>destroy<span style="color:#cccccc">;</span>
    <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span>nextDeps <span style="color:#67cdcc">!==</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
      <span style="color:#cc99cd">const</span> prevDeps <span style="color:#67cdcc">=</span> prevEffect<span style="color:#cccccc">.</span>deps<span style="color:#cccccc">;</span>
      <span style="color:#999999">// 对比依赖对象,是否发生更新,没有更新就复用nextDeps</span>
      <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span><span style="color:#f08d49">areHookInputsEqual</span><span style="color:#cccccc">(</span>nextDeps<span style="color:#cccccc">,</span> prevDeps<span style="color:#cccccc">)</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
        <span style="color:#f08d49">pushEffect</span><span style="color:#cccccc">(</span>hookFlags<span style="color:#cccccc">,</span> create<span style="color:#cccccc">,</span> destroy<span style="color:#cccccc">,</span> nextDeps<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
        <span style="color:#cc99cd">return</span><span style="color:#cccccc">;</span>
      <span style="color:#cccccc">}</span>
    <span style="color:#cccccc">}</span>
  <span style="color:#cccccc">}</span>

  currentlyRenderingFiber<span style="color:#cccccc">.</span>flags <span style="color:#67cdcc">|=</span> fiberFlags<span style="color:#cccccc">;</span>
<span style="color:#999999">// deps里发生更新,就创建新的effect对象</span>
  hook<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">=</span> <span style="color:#f08d49">pushEffect</span><span style="color:#cccccc">(</span>
    HookHasEffect <span style="color:#67cdcc">|</span> hookFlags<span style="color:#cccccc">,</span>
    create<span style="color:#cccccc">,</span>
    destroy<span style="color:#cccccc">,</span>
    nextDeps<span style="color:#cccccc">,</span>
  <span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>

</code></span></span></span>

#2.3 useRef

#mountRef (mount阶段)

看起来很简单,就是把initialValue 赋值给hook.memoizedState, 所以说只要弄懂useState、useEffect ,其他的看一眼就明白

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#999999">/**
 * @param {any} initialValue - 初始化值
 * 
*/</span>
<span style="color:#cc99cd">function</span> <span style="color:#f08d49">mountRef</span><span style="color:#cccccc">(</span>initialValue<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> hook <span style="color:#67cdcc">=</span> <span style="color:#f08d49">mountWorkInProgressHook</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">const</span> ref <span style="color:#67cdcc">=</span>  initialValue<span style="color:#cccccc">;</span>
  hook<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">=</span> ref<span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">return</span> ref<span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>
#updateRef (update阶段)

拿到上一次的值并返回

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#999999">/**
 * @param {any} initialValue - 初始化值
 * 
*/</span>
<span style="color:#cc99cd">function</span> <span style="color:#f08d49">updateRef</span><span style="color:#cccccc">(</span>initialValue<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> hook <span style="color:#67cdcc">=</span> <span style="color:#f08d49">mountWorkInProgressHook</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">const</span> ref <span style="color:#67cdcc">=</span>  initialValue<span style="color:#cccccc">;</span>
  hook<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">=</span> ref<span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">return</span> ref<span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>

#2.4 useCallback

#mountCallback (mount阶段)

把函数和依赖数组存到hook.memoizedState,并返回函数

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#999999">/**
 * @param {function} callback - 函数
 * @param {Array} deps - 依赖数组
 * @return {function} callback
*/</span>

<span style="color:#cc99cd">function</span> <span style="color:#f08d49">mountCallback</span><span style="color:#cccccc">(</span>callback<span style="color:#cccccc">,</span> deps<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> hook <span style="color:#67cdcc">=</span> <span style="color:#f08d49">mountWorkInProgressHook</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">const</span> nextDeps <span style="color:#67cdcc">=</span> deps <span style="color:#67cdcc">===</span> <span style="color:#cc99cd">undefined</span> <span style="color:#67cdcc">?</span> <span style="color:#cc99cd">null</span> <span style="color:#67cdcc">:</span> deps<span style="color:#cccccc">;</span>
  hook<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">=</span> <span style="color:#cccccc">[</span>callback<span style="color:#cccccc">,</span> nextDeps<span style="color:#cccccc">]</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">return</span> callback<span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>
#updateCallback (update阶段)

对比依赖是否变化,变化就返回最新的函数,没有变化就返回上一个函数

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#999999">/**
 * @param {function} callback - 函数
 * @param {Array} deps - 依赖数组
 * @return {function} callback
 * 
*/</span>
<span style="color:#cc99cd">function</span> <span style="color:#f08d49">updateCallback</span><span style="color:#cccccc">(</span>callback<span style="color:#cccccc">,</span> deps<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> hook <span style="color:#67cdcc">=</span> <span style="color:#f08d49">updateWorkInProgressHook</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">const</span> nextDeps <span style="color:#67cdcc">=</span> deps <span style="color:#67cdcc">===</span> <span style="color:#cc99cd">undefined</span> <span style="color:#67cdcc">?</span> <span style="color:#cc99cd">null</span> <span style="color:#67cdcc">:</span> deps<span style="color:#cccccc">;</span>
  <span style="color:#999999">// prevState:[callback, nextDeps]</span>
  <span style="color:#cc99cd">const</span> prevState <span style="color:#67cdcc">=</span> hook<span style="color:#cccccc">.</span>memoizedState<span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span>prevState <span style="color:#67cdcc">!==</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span>nextDeps <span style="color:#67cdcc">!==</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>

      <span style="color:#cc99cd">const</span> prevDeps <span style="color:#67cdcc">=</span> prevState<span style="color:#cccccc">[</span><span style="color:#f08d49">1</span><span style="color:#cccccc">]</span><span style="color:#cccccc">;</span>
      <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span><span style="color:#f08d49">areHookInputsEqual</span><span style="color:#cccccc">(</span>nextDeps<span style="color:#cccccc">,</span> prevDeps<span style="color:#cccccc">)</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
        <span style="color:#cc99cd">return</span> prevState<span style="color:#cccccc">[</span><span style="color:#f08d49">0</span><span style="color:#cccccc">]</span><span style="color:#cccccc">;</span>
      <span style="color:#cccccc">}</span>
    <span style="color:#cccccc">}</span>
  <span style="color:#cccccc">}</span>
  hook<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">=</span> <span style="color:#cccccc">[</span>callback<span style="color:#cccccc">,</span> nextDeps<span style="color:#cccccc">]</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">return</span> callback<span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>

#2.5 useMemo

#mountMemo (mount阶段)

调用传入函数拿到返回值,把值和依赖数组存到hook.memoizedState,并返回值

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#999999">/**
 * @param {function} nextCreate - 函数
 * @param {Array} deps - 依赖数组
 * @return {any} nextValue
 * 
*/</span>

<span style="color:#cc99cd">function</span> <span style="color:#f08d49">mountMemo</span><span style="color:#cccccc">(</span>
  nextCreate<span style="color:#cccccc">,</span>
  deps<span style="color:#cccccc">,</span>
<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> hook <span style="color:#67cdcc">=</span> <span style="color:#f08d49">mountWorkInProgressHook</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">const</span> nextDeps <span style="color:#67cdcc">=</span> deps <span style="color:#67cdcc">===</span> <span style="color:#cc99cd">undefined</span> <span style="color:#67cdcc">?</span> <span style="color:#cc99cd">null</span> <span style="color:#67cdcc">:</span> deps<span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">const</span> nextValue <span style="color:#67cdcc">=</span> <span style="color:#f08d49">nextCreate</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  hook<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">=</span> <span style="color:#cccccc">[</span>nextValue<span style="color:#cccccc">,</span> nextDeps<span style="color:#cccccc">]</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">return</span> nextValue<span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>
#updateMemo (update阶段)

对比依赖是否变化,变化就返回最新的值,没有变化就返回上一个值

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#999999">/**
 * @param {function} callback - 函数
 * @param {Array} deps - 依赖数组
 * @return {any} nextValue
 * 
*/</span>

<span style="color:#cc99cd">function</span> <span style="color:#f08d49">updateMemo</span><span style="color:#cccccc">(</span>
  nextCreate<span style="color:#cccccc">,</span>
  deps<span style="color:#cccccc">,</span>
<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> hook <span style="color:#67cdcc">=</span> <span style="color:#f08d49">updateWorkInProgressHook</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">const</span> nextDeps <span style="color:#67cdcc">=</span> deps <span style="color:#67cdcc">===</span> <span style="color:#cc99cd">undefined</span> <span style="color:#67cdcc">?</span> <span style="color:#cc99cd">null</span> <span style="color:#67cdcc">:</span> deps<span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">const</span> prevState <span style="color:#67cdcc">=</span> hook<span style="color:#cccccc">.</span>memoizedState<span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span>prevState <span style="color:#67cdcc">!==</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
    <span style="color:#999999">// Assume these are defined. If they're not, areHookInputsEqual will warn.</span>
    <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span>nextDeps <span style="color:#67cdcc">!==</span> <span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
      <span style="color:#cc99cd">const</span> prevDeps <span style="color:#67cdcc">=</span> prevState<span style="color:#cccccc">[</span><span style="color:#f08d49">1</span><span style="color:#cccccc">]</span><span style="color:#cccccc">;</span>
      <span style="color:#cc99cd">if</span> <span style="color:#cccccc">(</span><span style="color:#f08d49">areHookInputsEqual</span><span style="color:#cccccc">(</span>nextDeps<span style="color:#cccccc">,</span> prevDeps<span style="color:#cccccc">)</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
        <span style="color:#cc99cd">return</span> prevState<span style="color:#cccccc">[</span><span style="color:#f08d49">0</span><span style="color:#cccccc">]</span><span style="color:#cccccc">;</span>
      <span style="color:#cccccc">}</span>
    <span style="color:#cccccc">}</span>
  <span style="color:#cccccc">}</span>
  <span style="color:#cc99cd">const</span> nextValue <span style="color:#67cdcc">=</span> <span style="color:#f08d49">nextCreate</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  hook<span style="color:#cccccc">.</span>memoizedState <span style="color:#67cdcc">=</span> <span style="color:#cccccc">[</span>nextValue<span style="color:#cccccc">,</span> nextDeps<span style="color:#cccccc">]</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">return</span> nextValue<span style="color:#cccccc">;</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>

#3 使用hooks会遇到的问题

react hooks遇到的问题

React Hooks完全上手指南

在工程中必须引入lint插件,并开启相应规则,避免踩坑。

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cccccc">{</span>
  <span style="color:#7ec699">"plugins"</span><span style="color:#67cdcc">:</span> <span style="color:#cccccc">[</span><span style="color:#7ec699">"react-hooks"</span><span style="color:#cccccc">]</span><span style="color:#cccccc">,</span>
  <span style="color:#7ec699">"rules"</span><span style="color:#67cdcc">:</span> <span style="color:#cccccc">{</span>
    <span style="color:#7ec699">"react-hooks/rules-of-hooks"</span><span style="color:#67cdcc">:</span> <span style="color:#7ec699">"error"</span><span style="color:#cccccc">,</span>
    <span style="color:#7ec699">"react-hooks/exhaustive-deps"</span><span style="color:#67cdcc">:</span> <span style="color:#7ec699">"warn"</span>
  <span style="color:#cccccc">}</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>

这2条规则,对于新手,这个过程可能是比较痛苦的,如果你觉得这2个规则对你编写代码造成了困扰,说明你还未完全掌握hooks,对于某写特殊场景,确实不需要「exhaustive-deps」,可在代码处加eslint-disable-next-line react-hooks/exhaustive-deps;切记只能禁止本处代码,不能偷懒把整个文件都禁了。

#3.1 useEffect相关问题

  1. 依赖变量问题
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">function</span> <span style="color:#f08d49">ErrorDemo</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> <span style="color:#cccccc">[</span>count<span style="color:#cccccc">,</span> setCount<span style="color:#cccccc">]</span> <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useState</span><span style="color:#cccccc">(</span><span style="color:#f08d49">0</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">const</span> dom <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useRef</span><span style="color:#cccccc">(</span><span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
    dom<span style="color:#cccccc">.</span>current<span style="color:#cccccc">.</span><span style="color:#f08d49">addEventListener</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'click'</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#f08d49">setCount</span><span style="color:#cccccc">(</span>count <span style="color:#67cdcc">+</span> <span style="color:#f08d49">1</span><span style="color:#cccccc">)</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">[</span>count<span style="color:#cccccc">]</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">return</span> <span style="color:#67cdcc"><</span>div ref<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>dom<span style="color:#cccccc">}</span><span style="color:#67cdcc">></span><span style="color:#cccccc">{</span>count<span style="color:#cccccc">}</span><span style="color:#67cdcc"><</span><span style="color:#67cdcc">/</span>div<span style="color:#67cdcc">></span><span style="color:#cccccc">;</span>
</code></span></span></span>

像这种情况,每次count变化都会重新绑定一次事件,那我们怎么解决呢?

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">function</span> <span style="color:#f08d49">ErrorDemo</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> <span style="color:#cccccc">[</span>count<span style="color:#cccccc">,</span> setCount<span style="color:#cccccc">]</span> <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useState</span><span style="color:#cccccc">(</span><span style="color:#f08d49">0</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">const</span> dom <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useRef</span><span style="color:#cccccc">(</span><span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
    dom<span style="color:#cccccc">.</span>current<span style="color:#cccccc">.</span><span style="color:#f08d49">addEventListener</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'click'</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#f08d49">setCount</span><span style="color:#cccccc">(</span>count <span style="color:#67cdcc">+</span> <span style="color:#f08d49">1</span><span style="color:#cccccc">)</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">[</span><span style="color:#cccccc">]</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">return</span> <span style="color:#67cdcc"><</span>div ref<span style="color:#67cdcc">=</span><span style="color:#cccccc">{</span>dom<span style="color:#cccccc">}</span><span style="color:#67cdcc">></span><span style="color:#cccccc">{</span>count<span style="color:#cccccc">}</span><span style="color:#67cdcc"><</span><span style="color:#67cdcc">/</span>div<span style="color:#67cdcc">></span><span style="color:#cccccc">;</span>
</code></span></span></span>

把依赖count变量去掉吗?如果把依赖去掉的话,意味着hooks只在组件挂载的时候运行一次,count的值永远不会超过1;因为在effect 执行时,我们会创建一个闭包,并将count的值保存在闭包当中,且初始值为0

#思路1:消除依赖
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code>  <span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
     <span style="color:#999999">// 在这不依赖于外部的 `count` 变量</span>
    dom<span style="color:#cccccc">.</span>current<span style="color:#cccccc">.</span><span style="color:#f08d49">addEventListener</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'click'</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#f08d49">setCount</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span>precount<span style="color:#cccccc">)</span><span style="color:#67cdcc">=></span><span style="color:#67cdcc">++</span>precount<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span> 
  <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">[</span><span style="color:#cccccc">]</span><span style="color:#cccccc">)</span> <span style="color:#999999">// 我们的 effect 不使用组件作用域中的任何变量</span>
</code></span></span></span>

setCount也可以接收一个函数,这样就不用依赖count了

#思路1: 重新绑定事件
<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code>  <span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">const</span> $dom <span style="color:#67cdcc">=</span> dom<span style="color:#cccccc">.</span>current<span style="color:#cccccc">;</span>
    <span style="color:#cc99cd">const</span> <span style="color:#f08d49">event</span> <span style="color:#67cdcc">=</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
      <span style="color:#f08d49">setCount</span><span style="color:#cccccc">(</span>count<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
    <span style="color:#cccccc">}</span><span style="color:#cccccc">;</span>
    $dom<span style="color:#cccccc">.</span><span style="color:#f08d49">addEventListener</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'click'</span><span style="color:#cccccc">,</span> event<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
    <span style="color:#cc99cd">return</span>  $dom<span style="color:#cccccc">.</span><span style="color:#f08d49">removeEventListener</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'click'</span><span style="color:#cccccc">,</span> event<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">[</span>count<span style="color:#cccccc">]</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
</code></span></span></span>
#思路2:ref

你可以 使用一个 ref 来保存一个可变的变量。然后你就可以对它进行读写了

当你实在找不到更好的办法的时候,才这么做,因为依赖的变更使组件变的难以预测

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code>  <span style="color:#cc99cd">const</span> <span style="color:#cccccc">[</span>count<span style="color:#cccccc">,</span> setCount<span style="color:#cccccc">]</span> <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useState</span><span style="color:#cccccc">(</span><span style="color:#f08d49">0</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">const</span> dom <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useRef</span><span style="color:#cccccc">(</span><span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cc99cd">const</span> countRef<span style="color:#67cdcc">=</span><span style="color:#f08d49">useRef</span><span style="color:#cccccc">(</span>count<span style="color:#cccccc">)</span>
  <span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
    countRef<span style="color:#cccccc">.</span>current<span style="color:#67cdcc">=</span>count
  <span style="color:#cccccc">}</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
     <span style="color:#999999">// 在任何时候读取最新的 count</span>
    dom<span style="color:#cccccc">.</span>current<span style="color:#cccccc">.</span><span style="color:#f08d49">addEventListener</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'click'</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#f08d49">setCount</span><span style="color:#cccccc">(</span>countRef<span style="color:#cccccc">.</span>current <span style="color:#67cdcc">+</span> <span style="color:#f08d49">1</span><span style="color:#cccccc">)</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">[</span><span style="color:#cccccc">]</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span> <span style="color:#999999">// 这个 effect 从不会重新执行</span>
</code></span></span></span>
  1. 依赖函数问题

只有 当函数(以及它所调用的函数)不引用 props、state 以及由它们衍生而来的值时,你才能放心地把它们从依赖列表中省略。下面这个案例有一个 Bug:

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">function</span> <span style="color:#f08d49">ProductPage</span><span style="color:#cccccc">(</span><span style="color:#cccccc">{</span> productId <span style="color:#cccccc">}</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> <span style="color:#cccccc">[</span>product<span style="color:#cccccc">,</span> setProduct<span style="color:#cccccc">]</span> <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useState</span><span style="color:#cccccc">(</span><span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>

  <span style="color:#cc99cd">async</span> <span style="color:#cc99cd">function</span> <span style="color:#f08d49">fetchProduct</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">const</span> response <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">await</span> <span style="color:#f08d49">fetch</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'http://myapi/product/'</span> <span style="color:#67cdcc">+</span> productId<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span> <span style="color:#999999">// 使用了 productId prop</span>
    <span style="color:#cc99cd">const</span> json <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">await</span> response<span style="color:#cccccc">.</span><span style="color:#f08d49">json</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
    <span style="color:#f08d49">setProduct</span><span style="color:#cccccc">(</span>json<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cccccc">}</span>

  <span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
    <span style="color:#f08d49">fetchProduct</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">[</span><span style="color:#cccccc">]</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span> <span style="color:#999999">// 🔴 这样是无效的,因为 `fetchProduct` 使用了 `productId`</span>
  <span style="color:#999999">// ...</span>
</code></span></span></span>
#思路1:推荐的修复方案是把那个函数移动到你的 effect 内部

这样就能很容易的看出来你的 effect 使用了哪些 props 和 state,并确保它们都被声明了:

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">function</span> <span style="color:#f08d49">ProductPage</span><span style="color:#cccccc">(</span><span style="color:#cccccc">{</span> productId <span style="color:#cccccc">}</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> <span style="color:#cccccc">[</span>product<span style="color:#cccccc">,</span> setProduct<span style="color:#cccccc">]</span> <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useState</span><span style="color:#cccccc">(</span><span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>

  <span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
    <span style="color:#999999">// 把这个函数移动到 effect 内部后,我们可以清楚地看到它用到的值。</span>
    <span style="color:#cc99cd">async</span> <span style="color:#cc99cd">function</span> <span style="color:#f08d49">fetchProduct</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
      <span style="color:#cc99cd">const</span> response <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">await</span> <span style="color:#f08d49">fetch</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'http://myapi/product/'</span> <span style="color:#67cdcc">+</span> productId<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
      <span style="color:#cc99cd">const</span> json <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">await</span> response<span style="color:#cccccc">.</span><span style="color:#f08d49">json</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
      <span style="color:#f08d49">setProduct</span><span style="color:#cccccc">(</span>json<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
    <span style="color:#cccccc">}</span>

    <span style="color:#f08d49">fetchProduct</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">[</span>productId<span style="color:#cccccc">]</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span> <span style="color:#999999">// ✅ 有效,因为我们的 effect 只用到了 productId</span>
  <span style="color:#999999">// ...</span>
<span style="color:#cccccc">}</span>
</code></span></span></span>
#思路2: useCallback

把函数加入 effect 的依赖但 把它的定义包裹 进 useCallback Hook。这就确保了它不随渲染而改变,除非 它自身 的依赖发生了改变

<span style="background-color:#282c34"><span style="color:#2c3e50"><span style="color:#cccccc"><code><span style="color:#cc99cd">function</span> <span style="color:#f08d49">ProductPage</span><span style="color:#cccccc">(</span><span style="color:#cccccc">{</span> productId <span style="color:#cccccc">}</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">const</span> <span style="color:#cccccc">[</span>product<span style="color:#cccccc">,</span> setProduct<span style="color:#cccccc">]</span> <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useState</span><span style="color:#cccccc">(</span><span style="color:#cc99cd">null</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>

  <span style="color:#cc99cd">const</span> fetchProduct <span style="color:#67cdcc">=</span> <span style="color:#f08d49">useCallback</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">const</span> response <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">await</span> <span style="color:#f08d49">fetch</span><span style="color:#cccccc">(</span><span style="color:#7ec699">'http://myapi/product/'</span> <span style="color:#67cdcc">+</span> productId<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span> <span style="color:#999999">// 使用了 productId prop</span>
    <span style="color:#cc99cd">const</span> json <span style="color:#67cdcc">=</span> <span style="color:#cc99cd">await</span> response<span style="color:#cccccc">.</span><span style="color:#f08d49">json</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
    <span style="color:#f08d49">setProduct</span><span style="color:#cccccc">(</span>json<span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cccccc">}</span>
  <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">[</span>productId<span style="color:#cccccc">]</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span> 
<span style="color:#cccccc">}</span>

  <span style="color:#f08d49">useEffect</span><span style="color:#cccccc">(</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">=></span> <span style="color:#cccccc">{</span>
    <span style="color:#f08d49">fetchProduct</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span>
  <span style="color:#cccccc">}</span><span style="color:#cccccc">,</span> <span style="color:#cccccc">[</span>fetchProduct<span style="color:#cccccc">]</span><span style="color:#cccccc">)</span><span style="color:#cccccc">;</span> </code></span></span></span>

相关推荐

  1. React-Hooks

    2023-12-12 08:00:05       33 阅读
  2. react hooks

    2023-12-12 08:00:05       38 阅读
  3. react Hooks

    2023-12-12 08:00:05       8 阅读
  4. ReactReact 内置 Hook

    2023-12-12 08:00:05       18 阅读
  5. react相关hooks(二)

    2023-12-12 08:00:05       36 阅读
  6. react hooks浅谈

    2023-12-12 08:00:05       39 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-12 08:00:05       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-12 08:00:05       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-12 08:00:05       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-12 08:00:05       18 阅读

热门阅读

  1. ARMday6作業

    2023-12-12 08:00:05       43 阅读
  2. ReactNative0.73发布,架构升级与更好的调试体验

    2023-12-12 08:00:05       43 阅读
  3. 后端开发面试题

    2023-12-12 08:00:05       40 阅读
  4. Win32 HIWORD和LOWORD宏学习

    2023-12-12 08:00:05       37 阅读
  5. axios无法获取response headers中的内容

    2023-12-12 08:00:05       39 阅读
  6. MATLAB中cell函数的用法

    2023-12-12 08:00:05       39 阅读
  7. pytorch环境配置

    2023-12-12 08:00:05       41 阅读
  8. [cleanrl] ppo_continuous_action源码解析

    2023-12-12 08:00:05       44 阅读
  9. Oracle 解决ORA-00257 Archiver error 报错

    2023-12-12 08:00:05       38 阅读