蜗牛如如不动 發表於 2021-3-4 19:02:00

干货满满-原来这才是hooks-React Hooks使用心得

<h2 id="%E5%BA%8F%E8%A8%80" class="mume-header">序言</h2>
<pre class="language-text"><span style="font-size: 14px">---最后有招聘信息哦~<br><br>React是一个库,它不是一个框架。用于构建用户界面的Javascript库。这里大家需要认识这一点。react的核心在于它仅仅是考虑了如何将dom节点更快更好更<br>合适的渲染到浏览器中。它本身提供的涉及框架的理念是不多的。class组件是如此,hooks组件也是如此。</span></pre>
<h2 id="classcomponent" class="mume-header">ClassComponent</h2>
<p>我们先回顾一下,这是一个react的class组件:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class HelloMessage extends React.Component {
constructor (props) {
      super(props)
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state =<span style="color: rgba(0, 0, 0, 1)"> {
          num: </span>1<span style="color: rgba(0, 0, 0, 1)">
      }
}
componentDidMount () {
      alert(</span>'mounted!'<span style="color: rgba(0, 0, 0, 1)">)
}
addNum () {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({
          num: </span><span style="color: rgba(0, 0, 255, 1)">this</span>.state.num + 1<span style="color: rgba(0, 0, 0, 1)">;
      })
}
render() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
      Hello {</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.name}
      </span>&lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    );
}
}

ReactDOM.render(
</span>&lt;HelloMessage name="Taylor" /&gt;,
document.getElementById('hello-example'<span style="color: rgba(0, 0, 0, 1)">)
);</span></pre>
</div>
<p>&nbsp;</p>
<p>它包含了:</p>
<ul>
<li>props</li>
<li>state</li>
<li>setState</li>
<li>render</li>
<li>生命周期</li>
</ul>
<p>再来看看class组件中比较特殊的,具有代表意义的东西:</p>
<p>1、setState</p>
<p><br>通过对象更新的方式更新组件状态,react通过diff算法自动更新dom</p>
<p>2、Ref</p>
<p><br>一个可以用来访问dom对象的东西</p>
<p>3、this</p>
<p><br>classComponent中通过this访问各种数据/方法</p>
<p><br>4、生命周期</p>
<p><img src="https://img2020.cnblogs.com/blog/893115/202103/893115-20210304185049032-1447138251.png" alt="" width="817" height="844" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h3>class组件存在的问题</h3>
<p>* 逻辑分散<br>* 数据分散<br>* 组件拆分繁琐,state树往往较大</p>
<p>我们构建React组件的方式与组件的生命周期是耦合的。这一鸿沟顺理成章的迫使整个组件中散布着相关的逻辑。在下面的示例中,我们可以清楚地了解到这一点。有三个的方法componentDidMount、componentDidUpdate<em id="__mceDel">和updateRepos</em><em id="__mceDel"><em id="__mceDel">来完成相同的任务——使repos与任何props.id同步。</em></em></p>
<p class="sync-line" data-line="65">&nbsp;</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">componentDidMount () {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.updateRepos(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.id)
}
componentDidUpdate (prevProps) {
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (prevProps.id !== <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.id) {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.updateRepos(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.id)
    }
}
updateRepos </span>= (id) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ loading: <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> })
    fetchRepos(id)
      .then((repos) </span>=&gt; <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({
      repos,
      loading: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
      }))
}</span></pre>
</div>
<p>&nbsp;</p>
<p>为了解决这个问题(它是一系列问题的其中之一,这里只简单举例),react创造了一个全新的方式来解决,那就是hooks。</p>
<p class="sync-line" data-line="85">&nbsp;</p>
<h2>FunctionalComponent</h2>
<p>hooks的目的是让大家在一定程度上&nbsp;<strong>“使用函数的想法去开发组件”</strong>&nbsp;。<br>纯函数组件、高阶组件<br><strong>先粗浅理解函数式</strong></p>
<p><img src="https://img2020.cnblogs.com/blog/893115/202103/893115-20210304185312030-1323506034.png" alt="" width="145" height="71" loading="lazy"></p>
<p><br>主要思想是把运算过程尽量写成一系列嵌套的函数调用。举例来说,现在有这样一个数学表达式:</p>
<div class="cnblogs_code">
<pre>(1 + 2) * 3 - 4</pre>
</div>
<p>&nbsp;</p>
<p>传统的过程式编程,可能这样写:</p>
<p class="sync-line" data-line="94">&nbsp;</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> a = 1 + 2<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 0, 255, 1)">var</span> b = a * 3<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 0, 255, 1)">var</span> c = b - 4;</pre>
</div>
<p>&nbsp;</p>
<p>函数式编程要求使用函数,我们可以把运算过程定义为不同的函数,然后写成下面这样:</p>
<p class="sync-line" data-line="102">&nbsp;</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> result = subtract(multiply(add(1,2), 3), 4);</pre>
</div>
<p>&nbsp;</p>
<p><strong>函数式的特点</strong></p>
<ol>
<li>
<p>函数是"第一等公民"</p>
</li>
<li>
<p>只用"表达式",不用"语句"</p>
</li>
</ol>
<p>"表达式"(expression)是一个单纯的运算过程,总是有返回值;"语句"(statement)是执行某种操作,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。</p>
<p>原因是函数式编程的开发动机,一开始就是为了处理运算(computation),不考虑系统的读写(I/O)。"语句"属于对系统的读写操作,所以就被排斥在外。</p>
<p>当然,实际应用中,不做I/O是不可能的。因此,编程过程中,函数式编程只要求把I/O限制到最小,不要有不必要的读写行为,保持计算过程的单纯性。</p>
<p><strong>纯函数组件</strong><br>FunctionComponent:</p>
<p>满足“不修改状态”, “引用透明” ,“组件一等公民”, “没有副作用”, “表达式</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;<img src="https://img2020.cnblogs.com/blog/893115/202103/893115-20210304185249615-836090381.png" alt="" width="461" height="150" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong>高阶组件</strong><br>higherOrderComponent(高阶组件):</p>
<p>传入一个组件,返回另一个包装组件,向其注入数据、逻辑,达到拆分组件的目的<br><img src="https://img2020.cnblogs.com/blog/893115/202103/893115-20210304185342650-420885285.png" alt="" width="598" height="185" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p class="sync-line" data-line="130">&nbsp;</p>
<div class="cnblogs_code">
<pre>import * as React from 'react'<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      ComponentA({ Com: ComponentB })
    )
}

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> ComponentA (
    { Com }: { Com: React.FunctionComponent</span>&lt;any&gt;<span style="color: rgba(0, 0, 0, 1)"> }
) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
            &lt;Com style={{ color: 'red' }} /&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    )
}

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> ComponentB ({ style }: {
    style: any
}) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div style={style}&gt;hello high order component&lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    )
}</span></pre>
</div>
<p>&nbsp;</p>
<p class="sync-line" data-line="159">&nbsp;</p>
<h2 id="hooks%E4%B8%8E%E5%AE%9E%E8%B7%B5" class="mume-header">Hooks与实践</h2>
<p>Hooks你可以理解为具备class组件功能的函数式组件——至少它的理想是函数式</p>
<p><strong>HooksAPI-</strong><em id="__mceDel"><strong>今天的重点不在这里,这里不去细细讲它</strong></em></p>
<div class="cnblogs_code">
<pre>1<span style="color: rgba(0, 0, 0, 1)">、useState

</span>2<span style="color: rgba(0, 0, 0, 1)">、useReducer

</span>3<span style="color: rgba(0, 0, 0, 1)">、useEffect

</span>4<span style="color: rgba(0, 0, 0, 1)">、useLayoutEffect

</span>5<span style="color: rgba(0, 0, 0, 1)">、useMemo

</span>6<span style="color: rgba(0, 0, 0, 1)">、useCallback

</span>7<span style="color: rgba(0, 0, 0, 1)">、useContext

</span>8、useRef</pre>
</div>
<p>&nbsp;</p>
<p><strong>Hooks基本规则</strong><br>1、只在最顶层使用 Hook(<strong>why? 后文提到</strong>)</p>
<p>不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。(如果你对此感到好奇,在下面会有更深入的解释。)</p>
<p>2、只在 React 函数中调用 Hook<br>3、严格控制函数的行数(相信我,超过500行的函数,任何人都会头秃)<br><strong>hooks生命周期对应</strong></p>
<p class="sync-line" data-line="188">&nbsp;</p>
<div class="cnblogs_code">
<pre>export <span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> HooksComponent () {
    const [ date, setDate ] </span>= React.useState(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date());
    const div </span>= React.useRef(<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);

    const tick </span>= React.useCallback(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      setDate(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date());
    }, [])

    const color </span>= React.useCallback(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (div.current) {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (div.current.style.cssText.indexOf('red') &gt; -1<span style="color: rgba(0, 0, 0, 1)">) {
                div.current.style.cssText </span>= 'background: green;'<span style="color: rgba(0, 0, 0, 1)">
            } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
                div.current.style.cssText </span>= 'background: red;'<span style="color: rgba(0, 0, 0, 1)">
            }
      }
    }, [ div ]);

    const timerID </span>= React.useMemo(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      console.log(</span>'==组件是否更新(概念有区别)=='<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> setInterval(
            () </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> tick(),
            </span>1000<span style="color: rgba(0, 0, 0, 1)">
      );
    }, [ div ])

    React.useEffect(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      console.log(</span>'==组件加载完成=='<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
            console.log(</span>'==组件将要销毁=='<span style="color: rgba(0, 0, 0, 1)">)
            clearInterval(timerID);
      }
    }, [])

    React.useEffect(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      console.log(</span>'==组件更新完成=='<span style="color: rgba(0, 0, 0, 1)">)
      color();
    }, [ date ])

    console.log(</span>'==组件将要加载=='<span style="color: rgba(0, 0, 0, 1)">)
    console.log(</span>'==组件获取新的props=='<span style="color: rgba(0, 0, 0, 1)">)
    console.log(</span>'==组件将要更新=='<span style="color: rgba(0, 0, 0, 1)">)


    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;DemoComponent /&gt;
<span style="color: rgba(0, 0, 0, 1)">    )
}</span></pre>
</div>
<p>&nbsp;</p>
<p><img src="https://img2020.cnblogs.com/blog/893115/202103/893115-20210304185502767-553587824.png" alt="" width="752" height="571" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p class="sync-line" data-line="240">&nbsp;</p>
<h2 id="hooks%E5%8E%9F%E7%90%86demo%E5%AE%9E%E7%8E%B0" class="mume-header">Hooks原理demo实现</h2>
<p>实现文件:</p>
<p class="sync-line" data-line="243">&nbsp;</p>
<div class="cnblogs_code">
<pre>import * as React from 'react'<span style="color: rgba(0, 0, 0, 1)">;

type State </span>=<span style="color: rgba(0, 0, 0, 1)"> {
    : any
}

export type Reducer </span>= (state: State, action: string) =&gt;<span style="color: rgba(0, 0, 0, 1)"> State;

export type UseReducer </span>= <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)"> demoReducer;

type Queue</span>&lt;T&gt; =<span style="color: rgba(0, 0, 0, 1)"> {
    root: Queue</span>&lt;T&gt; | <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">,
    next: Queue</span>&lt;T&gt; | <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">,
    prev: Queue</span>&lt;T&gt; | <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">,
    value: T,
    index: number
}

const memo: Queue</span>&lt;State&gt;[] =<span style="color: rgba(0, 0, 0, 1)"> [];

let index </span>= 0<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 0, 255, 1)">function</span> demoReducer (reducer: Reducer, initalState?: State, update?: () =&gt; <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">) : any[] {
    let has </span>= !!<span style="color: rgba(0, 0, 0, 1)">memo;
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">memo) {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (index === 0<span style="color: rgba(0, 0, 0, 1)">) {
            memo[</span>0] = GeQueue&lt;State&gt;<span style="color: rgba(0, 0, 0, 1)">(initalState, index);
            memo[</span>0].root =memo;
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
            memo </span>= GeQueue&lt;State&gt;<span style="color: rgba(0, 0, 0, 1)">(initalState, index);
            memo.root </span>=memo;
            memo.prev </span>= memo;
            memo.next =<span style="color: rgba(0, 0, 0, 1)"> memo;
      }
      index </span>= index + 1<span style="color: rgba(0, 0, 0, 1)">;
    }
    let state;
    </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (has) {
      state </span>=<span style="color: rgba(0, 0, 0, 1)"> memo.value;
      index </span>= index + 1<span style="color: rgba(0, 0, 0, 1)">;
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
      state </span>= memo.value;
    }
    let bindex </span>=<span style="color: rgba(0, 0, 0, 1)"> index;
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> [
      state,
      (action: string) </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
            memo.value = reducer(memo.value, action)
            update();
      }
    ]
}

</span><span style="color: rgba(0, 0, 255, 1)">function</span> GeQueue&lt;T&gt;(value: T, index: number): Queue&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> {
      root: </span><span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">,
      prev: </span><span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">,
      next: </span><span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">,
      value,
      index
    }
}

export </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> DemoHoc ({ Com }: any) {
    const [ u, setU ] </span>= React.useState(1<span style="color: rgba(0, 0, 0, 1)">)
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;
            &lt;Com useReducer={(reducer: Reducer, initalState?: State) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> demoReducer(reducer, initalState, () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
                  index </span>= 0<span style="color: rgba(0, 0, 0, 1)">;
                  setU(u </span>+ 1<span style="color: rgba(0, 0, 0, 1)">);
                })
            }} </span>/&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    )
}</span></pre>
</div>
<p>&nbsp;</p>
<p>使用文件:</p>
<p class="sync-line" data-line="322">&nbsp;</p>
<div class="cnblogs_code">
<pre>import * as React from 'react'<span style="color: rgba(0, 0, 0, 1)">;
import { DemoHoc, UseReducer } from </span>'./useReducer/useReducerDemo'<span style="color: rgba(0, 0, 0, 1)">
import { Button } from </span>'antd'<span style="color: rgba(0, 0, 0, 1)">;

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> HookDemo () {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;DemoHoc Com={DemoComponent} /&gt;
<span style="color: rgba(0, 0, 0, 1)">    )
}

const initialState </span>= {count: 0<span style="color: rgba(0, 0, 0, 1)">};

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> reducer(state: any, action: any) {
</span><span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (action.type) {
    </span><span style="color: rgba(0, 0, 255, 1)">case</span> 'increment'<span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> {count: state.count + 1<span style="color: rgba(0, 0, 0, 1)">};
    </span><span style="color: rgba(0, 0, 255, 1)">case</span> 'decrement'<span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> {count: state.count - 1<span style="color: rgba(0, 0, 0, 1)">};
    </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Error();
}
}


</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> DemoComponent({ useReducer }: {
    useReducer: UseReducer
}) {
    const [ state, dispatch ] </span>=<span style="color: rgba(0, 0, 0, 1)"> useReducer(reducer, initialState);
    const [ state2, dispatch2 ] </span>=<span style="color: rgba(0, 0, 0, 1)"> useReducer(reducer, {...initialState});
   
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
            Count: {state.count}
            </span>&lt;Button onClick={() =&gt; dispatch({type: 'decrement'})}&gt;-&lt;/Button&gt;
            &lt;Button onClick={() =&gt; dispatch({type: 'increment'})}&gt;+&lt;/Button&gt;
            &lt;div&gt;&lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">            Count2: {state2.count}
            </span>&lt;Button onClick={() =&gt; dispatch2({type: 'decrement'})}&gt;-&lt;/Button&gt;
            &lt;Button onClick={() =&gt; dispatch2({type: 'increment'})}&gt;+&lt;/Button&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    )
}</span></pre>
</div>
<p>&nbsp;</p>
<p>原理:<br>我们先看实现文件的这个函数(不了解ts的同学,可以忽略ts的理解):</p>
<p class="sync-line" data-line="369">&nbsp;</p>
<div class="cnblogs_code">
<pre>const memo: Queue&lt;State&gt;[] =<span style="color: rgba(0, 0, 0, 1)"> [];

let index </span>= 0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">function</span> demoReducer (reducer: Reducer, initalState?: State, update?: () =&gt; <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">) : any[] {
    let has </span>= !!<span style="color: rgba(0, 0, 0, 1)">memo;
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">memo) {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (index === 0<span style="color: rgba(0, 0, 0, 1)">) {
            memo[</span>0] = GeQueue&lt;State&gt;<span style="color: rgba(0, 0, 0, 1)">(initalState, index);
            memo[</span>0].root =memo;
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
            memo </span>= GeQueue&lt;State&gt;<span style="color: rgba(0, 0, 0, 1)">(initalState, index);
            memo.root </span>=memo;
            memo.prev </span>= memo;
            memo.next =<span style="color: rgba(0, 0, 0, 1)"> memo;
      }
      index </span>= index + 1<span style="color: rgba(0, 0, 0, 1)">;
    }
    let state;
    </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (has) {
      state </span>=<span style="color: rgba(0, 0, 0, 1)"> memo.value;
      index </span>= index + 1<span style="color: rgba(0, 0, 0, 1)">;
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
      state </span>= memo.value;
    }
    let bindex </span>=<span style="color: rgba(0, 0, 0, 1)"> index;
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> [
      state,
      (action: string) </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
            memo.value = reducer(memo.value, action)
            update();
      }
    ]
}</span></pre>
</div>
<p>&nbsp;</p>
<ul>
<li>最开始,我们用了一个memo对象(它是一个队列)这里简单的定义成数组。 然后又又一个index,记录数字关系</li>
<li>然后重点来了</li>
</ul>
<p class="sync-line" data-line="406">&nbsp;</p>
<div class="cnblogs_code">
<pre>let has = !!<span style="color: rgba(0, 0, 0, 1)">memo;
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">memo) {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (index === 0<span style="color: rgba(0, 0, 0, 1)">) {
            memo[</span>0] = GeQueue&lt;State&gt;<span style="color: rgba(0, 0, 0, 1)">(initalState, index);
            memo[</span>0].root =memo;
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
            memo </span>= GeQueue&lt;State&gt;<span style="color: rgba(0, 0, 0, 1)">(initalState, index);
            memo.root </span>=memo;
            memo.prev </span>= memo;
            memo.next =<span style="color: rgba(0, 0, 0, 1)"> memo;
      }
      index </span>= index + 1<span style="color: rgba(0, 0, 0, 1)">;
    }
    let state;
    </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (has) {
      state </span>=<span style="color: rgba(0, 0, 0, 1)"> memo.value;
      index </span>= index + 1<span style="color: rgba(0, 0, 0, 1)">;
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
      state </span>= memo.value;
    }
    let bindex </span>= index;</pre>
</div>
<p>&nbsp;</p>
<p>我们调用useReducer的时候,从memo中尝试获取index的值,若存在的话,将state赋值为当前memo里的值并返回</p>
<ul>
<li>最后我们提供了一个触发更新的函数</li>
</ul>
<p>然后-我们再看看使用的代码:</p>
<p class="sync-line" data-line="434">&nbsp;</p>
<div class="cnblogs_code">
<pre>const initialState = {count: 0<span style="color: rgba(0, 0, 0, 1)">};

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> reducer(state: any, action: any) {
</span><span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (action.type) {
    </span><span style="color: rgba(0, 0, 255, 1)">case</span> 'increment'<span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> {count: state.count + 1<span style="color: rgba(0, 0, 0, 1)">};
    </span><span style="color: rgba(0, 0, 255, 1)">case</span> 'decrement'<span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> {count: state.count - 1<span style="color: rgba(0, 0, 0, 1)">};
    </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Error();
}
}


</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> DemoComponent({ useReducer }: {
    useReducer: UseReducer
}) {
    const [ state, dispatch ] </span>=<span style="color: rgba(0, 0, 0, 1)"> useReducer(reducer, initialState);
    const [ state2, dispatch2 ] </span>=<span style="color: rgba(0, 0, 0, 1)"> useReducer(reducer, {...initialState});
   
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;div&gt;<span style="color: rgba(0, 0, 0, 1)">
            Count: {state.count}
            </span>&lt;Button onClick={() =&gt; dispatch({type: 'decrement'})}&gt;-&lt;/Button&gt;
            &lt;Button onClick={() =&gt; dispatch({type: 'increment'})}&gt;+&lt;/Button&gt;
            &lt;div&gt;&lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">            Count2: {state2.count}
            </span>&lt;Button onClick={() =&gt; dispatch2({type: 'decrement'})}&gt;-&lt;/Button&gt;
            &lt;Button onClick={() =&gt; dispatch2({type: 'increment'})}&gt;+&lt;/Button&gt;
      &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">    )
}</span></pre>
</div>
<p>&nbsp;</p>
<ul>
<li>useReducer时经过memo的存储,我们在队列结构里存下了值,当dispatch的时候,memo中的变量将会改变。</li>
</ul>
<p>所以各位看出来了吗?</p>
<h3>这就是hooks隐藏的逻辑所在!</h3>
<p>重点来了~<br><strong>hooks的本质是什么?</strong></p>
<p class="sync-line" data-line="478">&nbsp;注:我的实现是为了理解它,与官方源码存在出入哈,理解原理就好~</p>
<h2 id="hooks%E6%9C%AC%E8%B4%A8%E7%9C%8B%E5%9D%91%E4%B8%8E%E8%A7%84%E8%8C%83" class="mume-header">Hooks本质看坑与规范</h2>
<p>hooks的本质是-闭包。<br>它在外部作用域存储了值。<br>当setState发生时,整个函数(相当于render)都被触发调用,那么所有的变量就会重新赋值就像是下面的关键字:</p>
<ul>
<li>const</li>
<li>let</li>
<li>function<br>好多同学会觉得,奇怪了,为啥这个函数被重新调用,不会重新赋值呢?<br>关键就是闭包!<br>事实上hooks的每个setState都会导致function变成一个片段。在这之前的变量并不是之后的变量。<br>而我们在onclick等事件中引用的函数,有可能会存储旧的变量引用!这就会导致一些大问题!!!</li>
<li>例如内存泄漏</li>
<li>例如值的引用无效<br>那怎么解决?<br>react其实提供了解决办法,就是useMemo useCallback等等。你可以再深入的去理解它。</li>





</ul>
<p><strong>Hooks使用大忌</strong></p>
<p>1、 外部顶层变量问题,不要把全局变量放在函数外部,特别是你在开发公共组件的时候,你可以用useRef<br>2、依赖项不正确问题,依赖项不正确会导致闭包问题。<br>3、组件拆分太粗问题,函数不建议太长,500行以内最佳<br>4、盲目使用usexxx,要深刻理解才能更好的使用hooks</p>
<p class="sync-line" data-line="502">&nbsp;</p>
<h2 id="%E6%9C%80%E5%90%8E" class="mume-header">最后</h2>
<p>高效编码;健康生活;</p>
<p>招人啦招人啦,SHEIN前端岗后端岗等你来投,可以私信博主,也可以扫码查看。期待与你做同事!</p>
<p><img src="https://img2020.cnblogs.com/blog/893115/202103/893115-20210304185950925-1183937448.png" alt="" width="494" height="879" loading="lazy"></p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    ========================================================
转载请注明出处。<br><br>
来源:https://www.cnblogs.com/ztfjs/p/hooks.html
頁: [1]
查看完整版本: 干货满满-原来这才是hooks-React Hooks使用心得