React-hooks

news2024/11/25 20:24:57

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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1301324.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构高阶】红黑树

目录 一、红黑树的概念 二、红黑树的性质 2.1 红黑树与AVL树的比较 三、红黑树的实现 3.1 红黑树节点的定义 3.2 数据的插入 3.2.1 红黑树的调整思路 3.2.1.1 cur为红&#xff0c;f为红&#xff0c;g为黑&#xff0c;u存在且为红 3.2.1.2 cur为红&#xff0c;f为红&am…

Seata配置

参考教程 seata 分布式事务的环境搭建与使用 Seata 1.4.0 nacos配置和使用&#xff0c;超详细 Seata 1.4.2 的安装 Nacos的配置和使用 官网下载地址 本文以v1.4.1为例 1.数据库及表的创建 创建seata数据库&#xff0c;创建以下表&#xff08;右键连接-》新建数据库seata-》…

MVC Gantt Wrapper:RadiantQ jQuery

The RadiantQ jQuery Gantt Package includes fully functional native MVC Wrappers that let you declaratively and seamlessly configure the Gantt component within your aspx or cshtm pages just like any other MVC extensions. 如果您还没有准备好转向完全基于客户端…

天池SQL训练营(二)-SQL基础查询与排序

-天池龙珠计划SQL训练营 Task02&#xff1a;SQL基础查询与排序 SQL训练营页面地址&#xff1a;https://tianchi.aliyun.com/specials/promotion/aicampsql 一、SELECT语句基础 1.1 从表中选取数据 SELECT语句 从表中选取数据时需要使用SELECT语句&#xff0c;也就是只从表…

unity 2d 入门 飞翔小鸟 死亡 显示GameOver(十四)

1、添加Img create->ui->img 把图片拖进去 2、和分数一样、调整位置 3、修改角色脚本 using System.Collections; using System.Collections.Generic; using UnityEngine;public class Fly : MonoBehaviour {//获取小鸟&#xff08;刚体&#xff09;private Rigidbod…

题目:肖恩的乘法表(蓝桥OJ 3404)

题目描述&#xff1a; 解题思路&#xff1a; 本题采用二分中的二分答案。且本题check()用不到开数组&#xff0c;所以不需要开数组&#xff0c;脑海中想象一个数组就好了 题解&#xff1a; #include<bits/stdc.h> using namespace std; using ll long long;ll n, m , k…

C# 图解教程 第5版 —— 第17章 转换

文章目录 17.1 什么是转换17.2 隐式转换17.3 显示转换和强制转换17.4 转换的类型17.5 数字的转换17.5.1 隐式数字转换17.5.2 溢出检测上下文17.5.3 显示数字转换 17.6 引用转换17.6.1 隐式引用转换17.6.2 显式引用转换17.6.3 有效显式引用转换 17.7 装箱转换17.7.1 装箱是创建副…

Python 全栈体系【四阶】(三)

第三章 matplotlib 一、基本绘图 1. 绘图核心 API 案例&#xff1a; 绘制简单直线 import numpy as np import matplotlib.pyplot as plt# 绘制简单直线 x np.array([1, 2, 3, 4, 5]) y np.array([3, 6, 9, 12, 15])plt.plot(x, y) plt.show() # 显示图片&#xff0c;阻塞…

《Mamba: Linear-Time Sequence Modeling with Selective State Spaces》阅读笔记

论文标题 《Mamba: Linear-Time Sequence Modeling with Selective State Spaces》 作者 Albert Gu 和 Tri Dao 初读 摘要 Transformer 架构及其核心注意力模块 地位&#xff1a;目前深度学习领域普遍的基础模型。 为了解决 Transformers 在长序列上的计算效率低下的问题…

【视频笔记】古人智慧与修行

古人的智慧 相由心生、老子悟道、佛祖成佛 多一些思考&#xff0c;多一些精神修炼。 除非我们今天能够产生与人类科技发展相并行的精神变革&#xff0c;否则永远可能也无法跳脱出历史的轮回。 视频来源 曾仕强教授周易的智慧 太极两仪四象八卦 一生二&#xff0c;二生三&…

gRPC基本用法:以人脸识别为例,搭建一个简单的gRPC服务

标题 0. gRPC简介 相关网站&#xff1a; 中文文档&#xff1a;gRPC 官方文档中文版_V1.0 官网&#xff1a;gRPC 介绍&#xff08;以下引自官方文档中文版中的介绍&#xff09;&#xff1a; gRPC是一个高性能、开源和通用的 RPC 框架&#xff0c;面向移动和 HTTP/2 设计。目前提…

LANDSAT_7/02/T1/RAW的Landsat7_C2_RAW类数据集

Landsat7_C2_RAW是指Landsat 7卫星的数据集&#xff0c;采用的是Collection 2级别的数据处理方法&#xff0c;对应的是Tier 1级别的原始数据&#xff08;RAW&#xff09;。该数据集包括了Landsat 7卫星从1999年4月15日开始的所有数据&#xff0c;共涵盖了全球范围内的陆地和海洋…

性格内向怎么办?如何改变性格内向?

性格内向和外向并没有优劣之分&#xff0c;性格内向也不是坏事&#xff0c;但从人际交往和事务处理的角度来看&#xff0c;内向性格确实不如外向的有优势&#xff0c;所以这也是很多人希望改变自己内向性格的原因。 此外性格内向也容易称为心理问题的替代词&#xff0c;比如&a…

Data Mining数据挖掘—2. Classification分类

3. Classification Given a collection of records (training set) – each record contains a set of attributes – one of the attributes is the class (label) that should be predicted Find a model for class attribute as a function of the values of other attribu…

【Linux】进程间通信之共享内存/消息队列/信号量

文章目录 一、共享内存的概念及原理二、共享内存相关接口说明1.shmget函数2.ftok函数3.shmat函数4.shmdt函数5.shmctl函数 三、用共享内存实现server&client通信1.shm_server.cc2.shm_client.cc3.comm.hpp4.查看ipc资源及其特征5.共享内存的优缺点6.共享内存的数据结构 四、…

ADAudit Plus:强大的网络安全卫士

随着数字化时代的不断发展&#xff0c;企业面临着越来越复杂和多样化的网络安全威胁。在这个信息爆炸的时代&#xff0c;保护组织的敏感信息和确保网络安全已经成为企业发展不可或缺的一环。为了更好地管理和监控网络安全&#xff0c;ADAudit Plus应运而生&#xff0c;成为网络…

【队列】数据也得排队

目录 引言 队列的概念 队列的实现 单向链表队列 结构 初始化 入队 出队 取队头 取队尾 求个数 判空 内存释放 总结 引言 队列&#xff0c;这个看似普通的数据结构&#xff0c;其实隐藏着无尽的趣味和巧思。就像单向链表这把神奇的魔法钥匙&#xff0c;它能打开队…

解决Git提交错误分支

如果 Git 提交到错误的分支&#xff0c;可以通过以下步骤将其转移到正确的分支上&#xff1a; 1.检查当前所在的分支&#xff0c;可以通过 git branch 命令查看。 git branch2.切换到正确的分支&#xff0c;可以通过 git checkout <正确的分支名> 命令进行切换。 git …

vue3-vite前端快速入门教程 vue-element-admin

Vue3快速入门学习 初始化项目 # 创建项目 npm create vitelatest my-vue-app -- --template vue # 安装依赖 npm i # 运行 npm run dev 模板语法 文本插值​ 最基本的数据绑定形式是文本插值&#xff0c;它使用的是“Mustache”语法 (即双大括号)&#xff1a; <span&g…

三相不平衡电压的正负序分析

1、什么是正负序&#xff1f; ABC 正序 ACB 负序 2、在abc坐标系下 接着利用矢量的旋转消去其它分量。。。 同理&#xff0c;得到其它的所有正负序的分量abc 3、在α/β坐标系下&#xff0c; 依次算出正负序的α/β来表示的abc 有一点需要特别注意&#xff0c;可以看到…