红爷 發表於 2019-11-28 20:42:00

React Hooks

<p>  React Hooks都是以use开头的函数,普通函数看待即可,接受参数,有返回值。只不过它们有特定的使用规则,只能在函数顶层使用,和特定的作用,比如useState使函数组件拥有了状态,useEffect处理组件的副作用</p>
<div class="cnblogs_code">
<pre>const App= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(''<span style="color: rgba(0, 0, 0, 1)">); // useState接受一个参数,返回一个数组,数组的第一项是值,第二项是更新值的函数。
}</span></pre>
</div>
<p>  声明两个变量来接受useState的返回值,<span style="color: rgba(0, 0, 0, 1)">message变量</span>获取到值,setMessage得到更新函数。更新函数的调用,React组件重新渲染(组件重新执行),再次调用useState,message变量获取到更改后的值,组件就有了状态。使用create-react-app&nbsp;创建项目,修改App.js</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">const App = () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
<span style="color: rgba(0, 0, 255, 1)">const = useState(<span style="color: rgba(128, 0, 0, 1)">''<span style="color: rgba(0, 0, 0, 1)">);

function handleChange(e) { setMessage(e.target.value)}
<span style="color: rgba(0, 0, 255, 1)">return<span style="color: rgba(0, 0, 0, 1)"> ( &lt;input value={message} onChange={handleChange}&gt;&lt;/input&gt; <span style="color: rgba(0, 0, 0, 1)">)
}</span></span></span></span></span></span></span></span></pre>
</div>
<p>  组件首次渲染,第一行调用useState(),返回了['', 更新函数],分别赋值给了message和setMessage。继续向下执行,创建函数<span style="color: rgba(0, 0, 255, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 255, 1)"><span style="color: rgba(128, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">handleChange</span></span></span></span></span>,再继续,返回一个jsx,message赋值给value, value的值为'',组件执行完成,页面上显示一个input&nbsp;输入框,值为空。此时,输入1,触发了onChange事件,调用setMessage,触发了React&nbsp;的更新机制。React不会立刻更新组件,而是把setMessage得到的新状态(1)放到更新队列中,React的渲染是异步的。当真正重新渲染时,React又会调用App函数组件,还是从上到下,一行一行执行代码。先调用useState(),此时useState返回的是[新的状态('1'), 更新函数],分别赋值给了message和setMessage,初始值(函数的参数)被忽略了。接着函数<span style="color: rgba(0, 0, 255, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 255, 1)"><span style="color: rgba(128, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">handleChange</span></span></span></span></span>创建,然后jsx,value是message, 是'1'。渲染完成,页面上input中显示1,组件状态改变了。当再输入2时,setMessage再次调用,新的状态(e.target.value('12'))再次存储。App组件再次被调用,还是先执行useSate(),返回最新的状态'12',赋值给message, 然后创建一个<span style="color: rgba(0, 0, 255, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 255, 1)"><span style="color: rgba(128, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">handleChange</span></span></span></span></span>函数,最后jsx中message取12, 组件渲染完成,页面中的input显示12. 整个过程如下</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 初始渲染。</span>
const message = '';<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> useState() 的调用</span>
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> handleChange(e) { setMessage(e.target.value)}
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (&lt;input value='' onChange={handleChange}&gt;&lt;/input&gt;)

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 输入1 更新渲染</span>
const message = '1';<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> useState() 的调用</span>
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> handleChange(e) { setMessage(e.target.value)}
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (&lt;input value='1' onChange={handleChange}&gt;&lt;/input&gt;)

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 再次输入2,更新渲染</span>
const message = '12';<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> useState() 的调用</span>
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> handleChange(e) { setMessage(e.target.value)}
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (&lt;input value='12' onChange={handleChange}&gt;&lt;/input&gt;)</pre>
</div>
<p>  组件每一次渲染,都会形成它自己独有的一个版本,在每次渲染中,都拥有着属于它本次渲染的状态和事件处理函数,每一次的渲染都是相互隔离,互不影响的,也就是说,在每一次的渲染中,如果jsx和事件处理函数中引用状态变量,它们仅仅能够获取到本次渲染中的状态变量的值,状态变量的值,在渲染开始时,调用useState就已经决定了。状态变量,只不过是每一次调用useState&nbsp;的返回值。React 负责状态的管理,而我们只是声明变量,使用状态。状态的更新,也不过是组件的重新渲染,React重新调用了组件函数,重新获取useState&nbsp;返回的值。useState() 返回的永远都是最新的状态值。Each Render Has Its Own Props and State,Each Render Has Its Own Event Handlers。</p>
<p>  useState的参数只在初次渲染时起作用,提供组件的初始状态。在以后的渲染中,不管是调用更新函数导致的组件渲染,还是父组件渲染导致子组件的渲染,参数都不会再用了,直接被忽略了,组件中的state状态变量,获取的都是最新值。useState可以接受函数初始化状态,毕竟只需初次渲染时执行一次。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">const</span> Message= (props) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">const</span> messageState = useState(() =&gt; {<span style="color: rgba(0, 0, 255, 1)">return</span> localstorage.getItem(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">name</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)}); // localStorage中去取数据作为初始状态
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> ... </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<p>&nbsp;  更新函数的参数还可以是函数,函数参数是当前状态值。如果想使用当前状态,生成一个新状态,就可以使用函数。</p>
<div class="cnblogs_code">
<pre>function <span style="color: rgba(0, 0, 0, 1)">handleChange(</span>e)<span style="color: rgba(0, 0, 0, 1)">{
   </span><span style="color: rgba(0, 0, 255, 1)">const</span> val =<span style="color: rgba(0, 0, 0, 1)"> e.target.value;
   setMessage(prev </span>=&gt; prev +<span style="color: rgba(0, 0, 0, 1)"> val);<br>} </span></pre>
</div>
<p>  当组件的状态是引用类型,比如数组和对象时,更新函数的参数,如果是值,必须接受一个全新的对象和数组,如果是回调函数,回调函数必须返回一个全新的对象和数组,因为React更新状态时,会用Object.js()对新旧状态进行比较,如果它俩相等,就不会重新渲染组件。对象的比较是引用(地址)的比较,改变对象的属性,地址不会变,新旧状态一样,React 不会重新渲染。两个完全不同的对象,地址当然不一样,新旧状态也就不一样。这也是状态的整体替换原则,使用新的状态去替换掉老的状态,而不是setState的合并原则。</p>
<div class="cnblogs_code">
<pre>const App = () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState({ message: '', id: 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;input value=<span style="color: rgba(0, 0, 0, 1)">{messageObj.message}
      onChange</span>={e =&gt;<span style="color: rgba(0, 0, 0, 1)"> {</span><span style="color: rgba(0, 0, 0, 1)">
          setMessage(prevState </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> { ...prevState, message: <span style="color: rgba(0, 0, 0, 1)">e.target.value</span> } <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回一个全新的对象</span>
<span style="color: rgba(0, 0, 0, 1)">          });
      }}
      </span>/&gt;
      &lt;p&gt;{messageObj.id} : {messageObj.message}&lt;/p&gt;
    &lt;/div&gt;
<span style="color: rgba(0, 0, 0, 1)">);
};</span></pre>
</div>
<p>  也正因为如此,React 建议把复杂的状态进行拆分,拆成一个一个单一的变量,更新的时候,只更新其中的某个或某些变量。就是使用多个useState(), 生成多个状态变量和更新函数。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">const</span> App = () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">const</span> = useState(<span style="color: rgba(128, 0, 0, 1)">''</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">const</span> = useState(<span style="color: rgba(128, 0, 128, 1)">1</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;div&gt;
            &lt;input value=<span style="color: rgba(0, 0, 0, 1)">{message} onChange</span>={e =&gt;<span style="color: rgba(0, 0, 0, 1)"> { setMessage(e.target.value); }} </span>/&gt;
            &lt;p&gt;{id} : {message}&lt;/p&gt;
      &lt;/div&gt;<span style="color: rgba(0, 0, 0, 1)">
    );
};</span></pre>
</div>
<p>  当然,复杂状态变量可以拆分,主要是每一个属性所代表的状态之间的关联不大。如果状态关联性特别强,就必须是一个复杂对象,建议使用useReducer.</p>
<p>  <span style="font-size: 18px"><strong>useEffect()</strong></span></p>
<p>  React的世界里,不是只有状态和改变状态,渲染组件,它还要和外界进行交互,比如,发送ajax请求,修改DOM,这些统称为副作用,因为React作用就是把state转化为UI,偏离主要功能的都是副作用。怎么理解副作用呢?可以把副作用想像成,组件渲染完成后,稍带要做的事情,主要任务做完了,稍带做点其他事情。怎么管理副作用?使用useEffect()。useEffect的第一个参数是回调函数,函数里面写副作用。组件渲染完成了,要改变dom,那就把改变dom的实现写到useEffect的回调函数中。</p>
<div class="cnblogs_code">
<pre>import React, { useEffect, useState } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">react</span><span style="color: rgba(128, 0, 0, 1)">'</span><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, 0, 1)"> function App() {
    </span><span style="color: rgba(0, 0, 255, 1)">const</span> = useState(<span style="color: rgba(128, 0, 0, 1)">''</span><span style="color: rgba(0, 0, 0, 1)">);

    function handleChange(e) { setMessage(e.target.value)}
    useEffect(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> { document.title </span>=<span style="color: rgba(0, 0, 0, 1)"> `${message}`;})

    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;input value={message} onChange={handleChange}&gt;&lt;/input&gt;<span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<p>  effect中是怎么获取到message的最新状态值的?和事件处理函数一样,每一次的渲染都有属于这一次渲染的effect函数,在这次渲染中,它能够获取到属于本次渲染状态值。每一次渲染,effect函数都不相同。React记住了effect函数,组件渲染完成后,就会调用它</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 初始渲染。</span>
const message = '';<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> useState() 的调用</span>
useEffect(() =&gt; { document.title = `${''}`;}) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 初始渲染的effect函数</span>

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 输入1 更新渲染</span>
const message = '1';<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> useState() 的调用</span>
useEffect(() =&gt; { document.title = `${1}`;}) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第二次渲染的effect函数</span>

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 再次输入2,更新渲染</span>
const message = '12';<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> useState() 的调用</span>
useEffect(() =&gt; { document.title = `${12}`;}) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第三次渲染的effect函数</span>  </pre>
</div>
<p>  总结一下React的渲染过程,React调用组件函数,state是'',组件返回&lt;input value='' onChange={handleChange}&gt;&lt;/input&gt;,同时告诉React,渲染完成后,执行()&nbsp;=&gt;&nbsp;{&nbsp;document.title&nbsp;=&nbsp;`${''}`;},React更新DOM,浏览器在屏幕上画出输入框,时机已到,React执行effect函数(()&nbsp;=&gt;&nbsp;{&nbsp;document.title&nbsp;=&nbsp;`${''}`;})。当在输入框输入1,调用setMessage,&nbsp;告诉React把状态更新为'1',React重新调用组件,state为'1',组件返回&lt;input value='1' onChange={handleChange}&gt;&lt;/input&gt;,同时告诉React,渲染完成后,执行()&nbsp;=&gt;&nbsp;{&nbsp;document.title&nbsp;=&nbsp;`${'1'}`;},React更新DOM,浏览器在屏幕上画出输入框,时机已到,React执行effect函数(()&nbsp;=&gt;&nbsp;{&nbsp;document.title&nbsp;=&nbsp;`${'1'}`;})。</p>
<p>  当useEffect的回调函数中返回一个函数时,这个函数称为清理函数</p>
<div class="cnblogs_code">
<pre>useEffect(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
function log() {console.log(message);}
window.addEventListener(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">resize</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, log);

</span><span style="color: rgba(0, 0, 255, 1)">return</span> () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    window.removeEventListener(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">resize</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, log);
}
})</span></pre>
</div>
<p>  初次渲染,useEffect&nbsp;就是告诉React,渲染完成后,执行&nbsp; &nbsp;</p>
<div class="cnblogs_code">
<pre>() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
function log() {console.log('');} </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 初次渲染时,message 为空</span><span style="color: rgba(0, 0, 0, 1)">
window.addEventListener(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">resize</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, log);

</span><span style="color: rgba(0, 0, 255, 1)">return</span> () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    window.removeEventListener(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">resize</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, log);
}</span></pre>
</div>
<p>  等到组件渲染到屏幕上时,effect函数执行,并注册了一个清理函数。组件再次渲染屏幕时,还是需要执行上面effect函数,不过上一次渲染完成后,注册了清理函数,所以先执行上一次渲染完成后注册的清理函数,再执行这一次渲染的effect函数,并再次注册一个清理函数。清理函数是在再次渲染完成后,需要执行effect函数前执行。需要注意的是,突然关闭页面,清理函数是不会执行的。但是在某些场景下,组件每次渲染后,都执行effect的函数,会带来问题,比如请求数据,组件渲染完成后,只请求一次就可以了。如下代码</p>
<div class="cnblogs_code">
<pre>import React, { useEffect, useState } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">react</span><span style="color: rgba(128, 0, 0, 1)">'</span><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, 0, 1)"> function App() {
    </span><span style="color: rgba(0, 0, 255, 1)">const</span> = useState(<span style="color: rgba(128, 0, 0, 1)">''</span><span style="color: rgba(0, 0, 0, 1)">);

    useEffect(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      fetch(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">https://jsonplaceholder.typicode.com/todos/1</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)
            .then(response </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> response.json())
            .then(json </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
                setMessage(json.title);
            })
    })
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;div&gt;{message}&lt;/div&gt;<span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<p>  打开控制台,发现不停地调用接口,状态更新会导致组件重新渲染,渲染完成,useEffect又会重新调用。请求数据-&gt; 更新状态-&gt;重新请求数据-&gt;更新状态,死循环了。这要用到useEffect的第二个参数,一个数组,用来告诉React ,再次渲染完成后,要不要调用useEffect中的函数。把useEffect回调函数中的要用到的外部变量,依次写到数组中,React就知道回调函数的执行是依赖这些变量,那么它就会时时地监听这些变量的变化,只要有更新,它就会重新调用回调函数。这个数组也称为依赖数组,回调函数要再次执行的依赖。函数的依赖发生变化,才会重新调用函数,函数的依赖没有变化,就不用调用函数。现在的回调函数fetch,&nbsp; 没有任何外部变量依赖,那就写一个空数组。空数组表示回调函数不依赖任何变量,没有依赖,永远不会变化,初次渲染完成后执行一次,以后更新就不用管了。</p>
<div class="cnblogs_code">
<pre>    useEffect(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      fetch(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">https://jsonplaceholder.typicode.com/todos/1</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)
      //...</span><span style="color: rgba(0, 0, 0, 1)">
    }, []) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 空数组,回调函数没有依赖作何外部的变量</span></pre>
</div>
<p>  如果组件属性或状态发生变化,都要执行useEffect,useEffect的回调函数就有依赖了,比如id变化,获取某个id的todos。</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)"> App() {
const </span>= useState(''<span style="color: rgba(0, 0, 0, 1)">);
const </span>= useState(1<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)"> handleId(e: any) { setId(e.target.value);}

useEffect(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      fetch(</span>'https://jsonplaceholder.typicode.com/todos/' +<span style="color: rgba(0, 0, 0, 1)"> id)
          .then(response </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> response.json())
          .then(json </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> { setTodoTitle(json.title); })
}, ) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 回调函数依赖了一个外部变量id</span>

<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">(
      </span>&lt;p&gt;id: &lt;input type='number' value={id} onChange={handleId}&gt;&lt;/input&gt;
<span style="color: rgba(0, 0, 0, 1)">          item title: {todoTitle}
      </span>&lt;/p&gt;
<span style="color: rgba(0, 0, 0, 1)">)
}</span></pre>
</div>
<p>  useEffect的回调函数中的变量、依赖数组中的变量和状态变量都是id,一一对应。只要在effect函数中用到了react数据流中的值,无论是props,state和函数,都要放到effect依赖数组中。其实effect回调函数还依赖setTotoTitle,但React&nbsp;保证 set*&nbsp;函数,在整个组件的渲染过程是都是一致的,可以不写。以下情况,可以把依赖去掉,而不会影响effect函数的执行。</p>
<div class="cnblogs_code">
<pre>useEffect(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">const</span> id = setInterval(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      setCount(count </span>+ <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">);
    }, </span><span style="color: rgba(128, 0, 128, 1)">1000</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)"> clearInterval(id);
}, );</span></pre>
</div>
<p>  转化成</p>
<div class="cnblogs_code">
<pre>useEffect(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">const</span> id = setInterval(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      setCount(c </span>=&gt; c + <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">); // <span style="color: rgba(0, 0, 0, 1)">c =&gt; c + <span style="color: rgba(128, 0, 128, 1)">1</span></span>,函数每次都会获取最新组件的状态给c
    }, </span><span style="color: rgba(128, 0, 128, 1)">1000</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)"> clearInterval(id);
}, []);</span></pre>
</div>
<p>&nbsp;  函数要不要定义到组件中,就看函数中有没有用到组件的属性和状态,如果没有,就和React无关,就可以把它放到组件外面,比如下面的函数</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">function getFetchUrl(query) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">https://hn.algolia.com/api/v1/search?query=</span><span style="color: rgba(128, 0, 0, 1)">'</span> +<span style="color: rgba(0, 0, 0, 1)"> query;
}
</span></pre>
</div>
<p>&nbsp;  useEffect的执行时机是渲染结果画到页面上后才执行,如果effect中操作dom,dom元素先出现到页面上,再修改,可能会造成闪动。如果不想要这种闪动,就要使用useLayoutEffect,useLayoutEffect则是DOM更新完成,还没有渲染到页面上时执行。</p>
<p><img src="https://img2024.cnblogs.com/blog/1013082/202411/1013082-20241111214122282-260383185.png"></p>
<p>&nbsp;  useState的简单理解:每一个组件都是一个fiber,它有一个memoizedState ,指向第一个useState,每一个useState都用链表链接起来</p>
<p><img src="https://img2024.cnblogs.com/blog/1013082/202411/1013082-20241120111611385-346476349.jpg"></p>
<p>&nbsp;  hook的调用顺序至关重要。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">const</span> Title = ({ flag }) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {

&nbsp;&nbsp;</span><span style="color: rgba(0, 0, 255, 1)">const</span> a = flag ? _useHook(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">a</span><span style="color: rgba(128, 0, 0, 1)">'</span>) : <span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">

&nbsp;&nbsp;</span><span style="color: rgba(0, 0, 255, 1)">const</span> b = _useHook(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">b</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)

&nbsp;&nbsp;</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;h1&gt;Hello World+{a}{b}&lt;/h1&gt;<span style="color: rgba(0, 0, 0, 1)">

}</span></pre>
</div>
<p><img src="https://img2024.cnblogs.com/blog/1013082/202411/1013082-20241120113922792-2005504196.jpg"></p>
<p>&nbsp;  那怎么实现上面的条件逻辑?</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">const</span> Title = ({ flag }) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {

&nbsp;&nbsp;</span><span style="color: rgba(0, 0, 255, 1)">const</span> _a = _useHook(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">a</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)

&nbsp;&nbsp;</span><span style="color: rgba(0, 0, 255, 1)">const</span> b = _useHook(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">b</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)

&nbsp;&nbsp;</span><span style="color: rgba(0, 0, 255, 1)">const</span> a = flag ? _a : <span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">

&nbsp;&nbsp;</span><span style="color: rgba(0, 0, 255, 1)">return</span> &lt;h1&gt;Hello World+{a}{b}&lt;/h1&gt;<span style="color: rgba(0, 0, 0, 1)">

}</span></pre>
</div>
<p>  useHook(_a)每次都会计算一次,但是可以不用。</p>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/SamWeb/p/11946418.html
頁: [1]
查看完整版本: React Hooks