跳進海裡躲雨 發表於 2020-12-23 14:33:00

React函数式组件值之useEffect()

<p><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">  Effect Hook 可以让你在函数组件中执行副作用操作,这里提到副作用,什么是副作用呢,就是除了状态相关的逻辑,比如网络请求,监听事件,查找 dom。</span></p>
<p><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">  可以这样说,在使用了useState或是useEffect这样的hooks之后,每次组件在render的时候都生成了一份本次render的state、function、effects,这些与之前或是之后的render里面的内容都是没有关系的。而对于class component来说,state是一种引用的形式。这就造成了二者在一些表现上的不同。</span></p>
<h1><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">一、基础用法</span></h1>
<p><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">  定义一个函数和一个数组。函数体为组件初始化或变化时执行的代码,返回值为组件销毁前执行的代码。数组参数中放的是触发此函数的依赖项数据。</span></p>
<div class="cnblogs_code">
<pre><span style="font-family: arial, helvetica, sans-serif; font-size: 16px"><span style="color: rgba(0, 128, 128, 1)">1</span> useEffect(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 相当于 componentDidMount、componentDidUpdate</span>
<span style="color: rgba(0, 128, 128, 1)">3</span>   console.log("code"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">4</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">5</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 相当于 componentWillUnmount</span>
<span style="color: rgba(0, 128, 128, 1)">6</span>   console.log("code"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">7</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">8</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></pre>
</div>
<h1><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">二、监听参数</span></h1>
<p><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">  类组件在绑定事件、解绑事件、设定定时器、查找 dom 的时候,是通过 componentDidMount、componentDidUpdate、componentWillUnmount 生命周期来实现的,而 useEffect 会在组件每次 render 之后调用,就相当于这三个生命周期函数,只不过可以通过传参来决定是否调用。</span></p>
<p><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">  其中注意的是,useEffect 会返回一个回调函数,作用于清除上一次副作用遗留下来的状态,如果该 useEffect 只调用一次,该回调函数相当于 componentWillUnmount 生命周期。</span></p>
<p><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">决定useEffect中各部分代码角色的是第二个参数:</span></p>
<ul>
<li><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">什么都不传,组件每次 render 之后 useEffect 都会调用,相当于 componentDidMount 和 componentDidUpdate</span></li>
<li><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">传入一个空数组 [], 只会调用一次,相当于 componentDidMount 和 componentWillUnmount</span></li>
<li><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">传入一个数组,其中包括变量,只有这些变量变动时,useEffect 才会执行</span></li>
</ul>
<div class="cnblogs_code">
<pre><span style="font-family: arial, helvetica, sans-serif; font-size: 16px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App () {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   const [ count, setCount ] = useState(0<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   const [ width, setWidth ] =<span style="color: rgba(0, 0, 0, 1)"> useState(document.body.clientWidth)
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   const onChange = () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">    setWidth(document.body.clientWidth)
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 7</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, 128, 1)"> 8</span>   useEffect(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">只有方法体,相当于componentDidMount和componentDidUpdate中的代码</span>
<span style="color: rgba(0, 128, 128, 1)">10</span>   document.title =<span style="color: rgba(0, 0, 0, 1)"> count;
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">})
</span><span style="color: rgba(0, 128, 128, 1)">12</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, 128, 1)">13</span>   useEffect(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">14</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">添加监听事件,相当于componentDidMount和componentDidUpdate中的代码</span>
<span style="color: rgba(0, 128, 128, 1)">15</span>   window.addEventListener('resize', onChange, <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">16</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">返回的函数用于解绑事件,相当于componentWillUnmount中的代码</span>
<span style="color: rgba(0, 128, 128, 1)">17</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">18</span>       window.removeEventListener('resize', onChange, <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">})
</span><span style="color: rgba(0, 128, 128, 1)">21</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, 128, 1)">22</span>   useEffect(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">23</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 相当于 componentDidMount</span>
<span style="color: rgba(0, 128, 128, 1)">24</span>   window.addEventListener('resize', onChange, <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">25</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">26</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 相当于 componentWillUnmount</span>
<span style="color: rgba(0, 128, 128, 1)">27</span>       window.removeEventListener('resize', onChange, <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">28</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 0, 1)">}, []);
</span><span style="color: rgba(0, 128, 128, 1)">30</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, 128, 1)">31</span>   useEffect(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">32</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">只有当count的值发生变化,此函数才会执行</span>
<span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 0, 1)">    console.log(`count change: count is ${count}`)
</span><span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)">}, [ count ]);
</span><span style="color: rgba(0, 128, 128, 1)">35</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 128, 128, 1)">36</span>   &lt;div&gt;
<span style="color: rgba(0, 128, 128, 1)">37</span> <span style="color: rgba(0, 0, 0, 1)">      页面名称: { count }
</span><span style="color: rgba(0, 128, 128, 1)">38</span> <span style="color: rgba(0, 0, 0, 1)">      页面宽度: { width }
</span><span style="color: rgba(0, 128, 128, 1)">39</span>       &lt;button onClick={() =&gt; { setCount(count + 1)}}&gt;点我&lt;/button&gt;
<span style="color: rgba(0, 128, 128, 1)">40</span>   &lt;/div&gt;
<span style="color: rgba(0, 128, 128, 1)">41</span> <span style="color: rgba(0, 0, 0, 1)">    )
</span><span style="color: rgba(0, 128, 128, 1)">42</span> }</span></pre>
</div>
<p><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">  其实Function Component 不存在生命周期,把 Class Component 的生命周期概念搬过来试图对号入座只是一种辅助记忆手段,Function Component 仅描述 UI 状态,React 会将其同步到 DOM,仅此而已。</span></p>
<h1><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">三、使用优化</span></h1>
<p><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">  我们在使用useState的时候,经常碰到capture value的问题,比如下面代码会输出5而不是3:</span></p>
<div class="cnblogs_code">
<pre><span style="font-family: arial, helvetica, sans-serif; font-size: 16px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> const App = () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   const = React.useState(5<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   const printTime = () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   setTimeout(() =&gt; console.log(temp), 3000<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">};
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   &lt;div onClick={() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">      printTime();
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>         setTemp(3<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">      }}
</span><span style="color: rgba(0, 128, 128, 1)">11</span>   &gt;clickMe&lt;/div&gt;
<span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">13</span> };</span></pre>
</div>
<p><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">  在printTime函数执行的那个 Render 过程里,temp 的值可以看作常量 5,执行 setTemp(3) 时会交由一个全新的 Render 渲染,所以不会执行printTime函数。而 3 秒后执行的内容是由 temp 为 5 的那个 Render 发出的,所以结果自然为 5。</span></p>
<p><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">  原因就是 temp、printTime 都拥有 Capture Value 特性。而useEffect 也一样具有 Capture Value 的特性。</span></p>
<p><span style="font-family: arial, helvetica, sans-serif; font-size: 16px">  利用 useRef 就可以绕过 Capture Value 的特性。可以认为 ref 在所有 Render 过程中保持着唯一引用,因此所有对 ref 的赋值或取值,拿到的都只有一个最终状态,而不会在每个 Render 间存在隔离。也可以简洁的认为,ref 是 Mutable 的,而 state 是 Immutable 的。</span></p>
<div class="cnblogs_code">
<pre><span style="font-family: arial, helvetica, sans-serif; font-size: 16px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Example() {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   const = useState(0<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   const latestCount =<span style="color: rgba(0, 0, 0, 1)"> useRef(count);
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   useEffect(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</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, 128, 1)"> 6</span>   latestCount.current =<span style="color: rgba(0, 0, 0, 1)"> count;
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   setTimeout(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</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, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">      console.log(`You clicked ${latestCount.current} times`);
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   }, 3000<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)">12</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 128, 128, 1)">13</span>   &lt;div&gt;
<span style="color: rgba(0, 128, 128, 1)">14</span>       &lt;p&gt;You clicked {count} times&lt;/p&gt;
<span style="color: rgba(0, 128, 128, 1)">15</span>       &lt;button onClick={() =&gt; setCount(count + 1)}&gt;Click me&lt;/button&gt;
<span style="color: rgba(0, 128, 128, 1)">16</span>   &lt;/div&gt;
<span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">18</span> }</span></pre>
</div>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/guanghe/p/14178482.html
頁: [1]
查看完整版本: React函数式组件值之useEffect()