<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    React Hooks 學(xué)習(xí)筆記 | useEffect Hook(二)

    共 27317字,需瀏覽 55分鐘

     ·

    2021-07-09 23:44

    大家好,上一篇文章我們學(xué)習(xí)了 State Hook 的基礎(chǔ)用法,還沒(méi)看的同學(xué)們,小編建議你先看下《 React Hooks 學(xué)習(xí)筆記 | State Hook(一)》這篇文章,今天我們一起來(lái)了解 useEffect Hook 的基礎(chǔ)用法。

    一、開(kāi)篇

    一般大多數(shù)的組件都需要特殊的操作,比如獲取數(shù)據(jù)、監(jiān)聽(tīng)數(shù)據(jù)變化或更改DOM的相關(guān)操作,這些操作被稱(chēng)作 “side effects(副作用)”。

    在類(lèi)組件中,我們通常會(huì)在 componentDidMount 和 componentDidUpdate 這兩個(gè)常用的生命鉤子函數(shù)進(jìn)行操作,這些生命周期的相關(guān)方法便于我們?cè)诤线m的時(shí)機(jī)更加精確的控制組件的行為。

    這有一個(gè)簡(jiǎn)單的代碼示例,頁(yè)面加載完成后,更改頁(yè)面的標(biāo)題

    componentDidMount() {
      document.title = this.state.name + " from " + this.state.location;
    }

    當(dāng)你嘗試更改標(biāo)題對(duì)應(yīng)的狀態(tài)值時(shí),頁(yè)面的標(biāo)題不會(huì)發(fā)生任何變化,你還需要添加另一個(gè)生命周期的方法 componentDidUpdate() ,監(jiān)聽(tīng)狀態(tài)值的變化重新re-render,示例代碼如下:

    componentDidUpdate() {
      document.title = this.state.name + " from " + this.state.location;
    }

    從上述代碼我們可以看出,要實(shí)現(xiàn)動(dòng)態(tài)更改頁(yè)面標(biāo)題的方法,我們需要調(diào)用兩個(gè)生命鉤子函數(shù),同樣的方法寫(xiě)兩遍。但是我們使用 useEffect Hook 函數(shù),就能解決代碼重復(fù)的問(wèn)題,示例代碼如下:

    import React, { useState, useEffect } from "react";
    //...
    useEffect(() => {
      document.title = name + " from " + location;
    });

    可以看出,使用 useEffect Hook ,我們就實(shí)現(xiàn)了兩個(gè)生命周期函數(shù)等效的目的,節(jié)省了代碼量。


    二、添加清除功能

    還有一個(gè)類(lèi)組件的例子,在某些情況下,你需要在組件卸載(unmounted)或銷(xiāo)毀(destroyed)之前,做一些有必要的清除的操作,比如timers、interval,或者取消網(wǎng)絡(luò)請(qǐng)求,或者清理任何在componentDidMount()中創(chuàng)建的DOM元素(elements),你可能會(huì)想到類(lèi)組件中的 componentWillUnmount()這個(gè)鉤子函數(shù),示例代碼如下:

    import React from "react";
    export default class ClassDemo extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          resolution: {
            widthwindow.innerWidth,
            heightwindow.innerHeight
          }
        };
        this.handleResize = this.handleResize.bind(this);
      }
      componentDidMount() {
        window.addEventListener("resize"this.handleResize);
      }
      componentDidUpdate() {
        window.addEventListener("resize"this.handleResize);
      }
      componentWillUnmount() {
        window.removeEventListener('resize'this.handleResize);
      }
      handleResize() {
        this.setState({
          resolution: {
            widthwindow.innerWidth,
            heightwindow.innerHeight
          }
        });
      }
      render() {
        return (
          <section>
            <h3>
              {this.state.resolution.width} x {this.state.resolution.height}
            </h3>
          </section>

        )
      }
    }

    上面的代碼將顯示瀏覽器窗口的當(dāng)前分辨率。當(dāng)你調(diào)整窗口大小,您應(yīng)該會(huì)看到自動(dòng)更新窗口的寬和高的值,同時(shí)我們又添加了組件銷(xiāo)毀時(shí),在 componentWillUnmount() 函數(shù)中定義清除監(jiān)聽(tīng)窗口大小的邏輯。

    如果我們使用 Hook 的方式改寫(xiě)上述代碼,看起來(lái)更加簡(jiǎn)潔,示例代碼如下:

    import React, { useState, useEffect } from "react";
    export default function HookDemo(props{
      ...
      const [resolution, setResolution] = useState({
        widthwindow.innerWidth,
        heightwindow.innerHeight
      });
      useEffect(() => {
        const handleResize = () => {
          setResolution({
            widthwindow.innerWidth,
            heightwindow.innerHeight
          });
        };
        window.addEventListener("resize", handleResize);
        // return clean-up function
        return () => {
          document.title = 'React Hooks Demo';
          window.removeEventListener("resize", handleResize);
        };
      });
      ...
      return (
        <section>
          ...
          <h3>
            {resolution.width} x {resolution.height}
          </h3>
        </section>

      );
    }

    運(yùn)行后的效果如下所示:

    顯而易見(jiàn),我們使用 hook 代碼完成了同樣的事情,代碼量更少,結(jié)構(gòu)更緊湊。你是否注意到我們?cè)谶@個(gè) useEffect Hook 中調(diào)用了 return 函數(shù)?這種寫(xiě)法就等同 componentWillUnmount(),你可以在這里做一些和清除邏輯相關(guān)的一些處理邏輯。

    三、關(guān)于 [ ] 依賴(lài)數(shù)組參數(shù)的說(shuō)明

    在開(kāi)篇的時(shí)候,我們使用  useEffect Hook 實(shí)現(xiàn)了 componentDidMount ,componentDidUpdate 兩個(gè)生命鉤子函數(shù)的一致的效果,這就意味著 DOM 加載完成后,狀態(tài)發(fā)生變化造成的 re-render 都會(huì)執(zhí)行 useEffect Hook 中的邏輯,在一些場(chǎng)景下,我們沒(méi)必要在狀態(tài)發(fā)生變化時(shí),調(diào)用此函數(shù)的邏輯,比如我們?cè)谶@里定義數(shù)據(jù)接口更改數(shù)據(jù)狀態(tài),數(shù)據(jù)狀態(tài)發(fā)生變化,會(huì)重新調(diào)用  useEffect Hook 中的請(qǐng)求邏輯,這樣豈不是進(jìn)入了無(wú)限循環(huán),數(shù)據(jù)量大的話(huà),說(shuō)不定就把接口請(qǐng)求死了。

    但是還好, useEffect Hook  提供了依賴(lài)使用參數(shù),第一個(gè)參數(shù)是定義方法,第二個(gè)參數(shù)是依賴(lài)數(shù)組,用于自定義依賴(lài)的參數(shù),是否觸發(fā)再次執(zhí)行,接下來(lái)我們來(lái)看幾個(gè)示例效果:

    3.1、after every render

    useEffect(() => { 
    // run after every rendering 
    console.log('render'
    })


    如上圖所示,我們每次更改狀態(tài)值導(dǎo)致組件重新渲染時(shí),我們?cè)?useEffect 中定義的輸出將會(huì)反復(fù)的被執(zhí)行。

    3.2、Once(執(zhí)行一次)

    接下來(lái)我們可以在第二個(gè)參數(shù)上定義一個(gè)空數(shù)組,解決上述問(wèn)題,告訴 Hook 組件只執(zhí)行一次(及時(shí)狀態(tài)發(fā)生改變導(dǎo)致的 re-render ),示例代碼如下:

    useEffect(() => {
        // Just run the first time
        console.log('render')
      }, [])


    如上圖運(yùn)行效果所示,你會(huì)發(fā)現(xiàn) Hook 函數(shù)中定義的輸出,無(wú)論我們?cè)趺锤臓顟B(tài)值,其只輸出一次。

    3.3、依賴(lài) state/props 的改變?cè)賵?zhí)行

    如果你想依賴(lài)特定的狀態(tài)值、屬性,如果其發(fā)生變化時(shí)導(dǎo)致的 re-render ,再次執(zhí)行 Hook 函數(shù)中定義的邏輯,你可以將其寫(xiě)在數(shù)組內(nèi),示例代碼如下:

    useEffect(() => {
        // When title or name changed will render
        console.log('render')
    }, [title, name])

    四、用一張圖總結(jié)下

    說(shuō)了這么多,我們做一下總結(jié),說(shuō)白了就是整合了 componentDidMount,componentDidUpdate,與 componentWillUnmount 這三個(gè)生命鉤子函數(shù),變成了一個(gè)API,其用法可以用如下一張圖進(jìn)行精簡(jiǎn)概括



    五、繼續(xù)完善購(gòu)物清單

    在上一篇系列文章里《 React Hooks 學(xué)習(xí)筆記 | State Hook(一)》,我們通過(guò)做一個(gè)簡(jiǎn)單的購(gòu)物清單實(shí)踐了 State Hook,本篇文章我們通過(guò)繼續(xù)完善這個(gè)實(shí)例,加深我們對(duì) useEffect Hook 的理解,學(xué)習(xí)之前大家可以先提前下載上一篇文章的源碼。

    本節(jié)案例,為了更加接近實(shí)際應(yīng)用場(chǎng)景,這里我使用了 Firebase 快速構(gòu)建后端的數(shù)據(jù)庫(kù)和其自身的接口服務(wù)。(谷歌的產(chǎn)品,目前需要科學(xué)上網(wǎng)才能使用,F(xiàn)irebase 是 Google Cloud Platform 為應(yīng)用開(kāi)發(fā)者們推出的應(yīng)用后臺(tái)服務(wù)。借助Firebase,應(yīng)用開(kāi)發(fā)者們可以快速搭建應(yīng)用后臺(tái),集中注意力在開(kāi)發(fā) client 上,并且可以享受到 Google Cloud 的穩(wěn)定性和 scalability )。

    5.1、創(chuàng)建Firebase

    1、在 https://firebase.google.com/(科學(xué)上網(wǎng)才能訪問(wèn)),使用谷歌賬戶(hù)登錄 ,進(jìn)入控制臺(tái)創(chuàng)建項(xiàng)目。

    2、這里我新建了一個(gè) react-hook-update 項(xiàng)目,并這個(gè)項(xiàng)目里我們創(chuàng)建了 Realtime Database 實(shí)時(shí)數(shù)據(jù)庫(kù)(非關(guān)系數(shù)據(jù)庫(kù)),用于存儲(chǔ)項(xiàng)目的數(shù)據(jù),其數(shù)據(jù)庫(kù)又提供了相關(guān)的接口用于數(shù)據(jù)的增刪改查。每個(gè)數(shù)據(jù)庫(kù)都會(huì)提供一個(gè)鏈接用于操作,本項(xiàng)目數(shù)據(jù)庫(kù)鏈接為 https://react-hook-update-350d4-default-rtdb.firebaseio.com/



    5.2、添加狀態(tài)加載、錯(cuò)誤提示UI組件

    接下來(lái)我們添加進(jìn)度加載組件和錯(cuò)誤提示對(duì)話(huà)框組件,分別用于狀態(tài)加載中狀態(tài)提示和系統(tǒng)錯(cuò)誤狀態(tài)提示,代碼比較簡(jiǎn)單,這里就是貼下相關(guān)代碼。

    LoadingIndicator 數(shù)據(jù)加載狀態(tài)提示組件

    import React from 'react';

    import './LoadingIndicator.css';

    const LoadingIndicator = () => (
        <div className="lds-ring">
            <div />
            <div />
            <div />
            <div />
        </div>

    );

    export default LoadingIndicator;

    // components/UI/LoadingIndicator.js
    .lds-ring {
      display: inline-block;
      position: relative;
      width54px;
      height54px;
    }
    .lds-ring div {
      box-sizing: border-box;
      display: block;
      position: absolute;
      width44px;
      height44px;
      margin6px;
      border6px solid #ff2058;
      border-radius50%;
      animation: lds-ring 1.2s cubic-bezier(0.500.51) infinite;
      border-color#ff2058 transparent transparent transparent;
    }
    .lds-ring div:nth-child(1) {
      animation-delay: -0.45s;
    }
    .lds-ring div:nth-child(2) {
      animation-delay: -0.3s;
    }
    .lds-ring div:nth-child(3) {
      animation-delay: -0.15s;
    }
    @keyframes lds-ring {
      0% {
        transformrotate(0deg);
      }
      100% {
        transformrotate(360deg);
      }
    }

    /*
     components/UI/LoadingIndicator.css
    */

    ErrorModal 系統(tǒng)錯(cuò)誤組件

    import React from "react";
    import './ErrorModal.css'

    const ErrorModal = React.memo(props => {
        return (
            <React.Fragment>
                <div className="backdrop" onClick={props.onClose} />
                <div className="error-modal">
                    <h2>An Error Occurred!</h2>
                    <p>{props.children}</p>
                    <div className="error-modal__actions">
                        <button type="button" onClick={props.onClose}>
                            Okay
                        </button>
                    </div>
                </div>
            </React.Fragment>

        );
    });

    export default ErrorModal;

    // components/UI/ErrorModal.js
    .error-modal {
      position: fixed;
      top30vh;
      leftcalc(50% - 15rem);
      width30rem;
      background: white;
      box-shadow0 2px 8px rgba(0000.26);
      z-index100;
      border-radius7px;
    }

    .error-modal h2 {
      margin0;
      padding1rem;
      background#ff2058;
      color: white;
      border-radius7px 7px 0 0;
    }

    .error-modal p {
        padding1rem;
    }

    .error-modal__actions {
        display: flex;
        justify-content: flex-end;
        padding0 0.5rem;
    }

    .backdrop {
      position: fixed;
      top0;
      left0;
      width100%;
      height100vh;
      backgroundrgba(0000.75);
      z-index50;
    }

    /*
     components/UI/ErrorModal.css
    */

    5.3、增加接口顯示購(gòu)物清單列表

    接下來(lái),我們?cè)谫?gòu)物清單頁(yè) Ingredients 組件里,我們使用今天所學(xué)的知識(shí),在 useEffect() 里添加歷史購(gòu)物清單的列表接口,用于顯示過(guò)往的清單信息,這里我們使用 firebase 的提供的API, 請(qǐng)求 https://react-hook-update-350d4-default-rtdb.firebaseio.com/ingredients.json  這個(gè)地址,就會(huì)默認(rèn)給你創(chuàng)建 ingredients 的集合,并返回一個(gè) JSON 形式的數(shù)據(jù)集合,示例代碼如下:

    useEffect(() => {
            fetch('https://react-hook-update-350d4-default-rtdb.firebaseio.com/ingredients.json')
                .then(response => response.json())
                .then(responseData => {
                    const loadedIngredients = [];
                    for (const key in responseData) {
                        loadedIngredients.push({
                            id: key,
                            title: responseData[key].title,
                            amount: responseData[key].amount
                        });
                    }
                    setUserIngredients(loadedIngredients);
                })
        }, []);

    // components/Ingredients/Ingredients.js

    上述代碼我們可以看出,我們使用 fetch 函數(shù)請(qǐng)求接口,請(qǐng)求完成后我們更新 UserIngredients 數(shù)據(jù)狀態(tài),最后別忘記了,同時(shí)在 useEffect 函數(shù)中,依賴(lài)參數(shù)為空數(shù)組[ ],表示只加載一次,數(shù)據(jù)狀態(tài)更新時(shí)導(dǎo)致的 re-render,就不會(huì)發(fā)生無(wú)限循環(huán)的請(qǐng)求接口了,這個(gè)很重要、很重要、很重要!

    5.4 、更新刪除清單的方法

    這里我們要改寫(xiě)刪除清單的方法,將刪除的數(shù)據(jù)更新到云端數(shù)據(jù)庫(kù) Firebase ,為了顯示更新?tīng)顟B(tài)和系統(tǒng)的錯(cuò)誤信息,這里我們引入 ErrorModal ,添加數(shù)據(jù)加載狀態(tài)和錯(cuò)誤狀態(tài),示例如下:

    import ErrorModal from '../UI/ErrorModal';
    const Ingredients = () => {
    ...
        const [isLoading, setIsLoading] = useState(false);
        const [error, setError] = useState();
    ...
    }

    // components/Ingredients/Ingredients.js

    接下來(lái)我們來(lái)改寫(xiě)刪除方法 removeIngredientHandler

    const removeIngredientHandler = ingredientId => {
            setIsLoading(true);
            fetch(`https://react-hook-update-350d4-default-rtdb.firebaseio.com/ingredients/${ingredientId}.json`,
                {
                    method'DELETE'
                }
            ).then(response => {
                setIsLoading(false);
                setUserIngredients(prevIngredients =>
                    prevIngredients.filter(ingredient => ingredient.id !== ingredientId)
                );
            }).catch(error => {
                setError('系統(tǒng)開(kāi)了個(gè)小差,請(qǐng)稍后再試!');
                setIsLoading(false);
            })
        };

    // components/Ingredients/Ingredients.js

    從上述代碼我們可以看出,首先我們先將加載狀態(tài)默認(rèn)為true,接下來(lái)請(qǐng)求刪除接口,這里請(qǐng)注意接口地址 ${ingredientId} 這個(gè)變量的使用(當(dāng)前數(shù)據(jù)的 ID 主鍵),刪除成功后,更新加載狀態(tài)為 false 。如果刪除過(guò)程中發(fā)生錯(cuò)誤,我們?cè)赾atch 代碼塊里捕捉錯(cuò)誤并調(diào)用錯(cuò)誤提示對(duì)話(huà)框(更新錯(cuò)誤狀態(tài)和加載狀態(tài))。

    5.5、更新添加清單的方法

    接著我們改寫(xiě)添加清單的方式,通過(guò)接口請(qǐng)求的方式,將添加的數(shù)據(jù)添加至 Firebase 數(shù)據(jù)庫(kù),代碼比較簡(jiǎn)單,就不多解釋了,示例代碼如下:

    const addIngredientHandler = ingredient => {
            setIsLoading(true);
            fetch('https://react-hook-update-350d4-default-rtdb.firebaseio.com/ingredients.json',
                {
                method'post',
                bodyJSON.stringify(ingredient),
                headers: {'Content-Type''application/json'}
            })
                .then(response => {
                setIsLoading(false);
                return response.json();
            })
                .then(responseData => {
                setUserIngredients(prevIngredients => [
                    ...prevIngredients,
                    {id: responseData.name, ...ingredient}
                ]);
            });

        };

    // components/Ingredients/Ingredients.js

    5.5、添加搜索組件功能

    我們繼續(xù)完善購(gòu)物清單的功能,為購(gòu)物清單添加新功能-搜索功能(通過(guò)請(qǐng)求接口),方便我們搜索清單的內(nèi)容,界面效果如下圖所示,在中間添加一個(gè)搜索框。

    新建 Search.js 文件,然后在 useEffect 方法內(nèi)通過(guò) Firebase 提供的接口,實(shí)現(xiàn)基于商品名稱(chēng)搜索購(gòu)物清單,然后定義 onLoadIngredients 方法屬性,用于接收返回的數(shù)據(jù),方便將數(shù)據(jù)通過(guò)參數(shù)的形式傳遞給父組件。最后我們定義 enteredFilter 數(shù)據(jù)狀態(tài),用于接收用戶(hù)輸入框的輸入內(nèi)容,代碼如下所示:
    import React,{useState,useEffect,useRef} from "react";
    import Card from "../UI/Card";
    import './Search.css';

    const Search = React.memo(props=>{
        const { onLoadIngredients } = props;
        const [enteredFilter,setEnterFilter]=useState('');
        const inputRef = useRef();
        useEffect(() => {
            const timer = setTimeout(() => {
                if (enteredFilter === inputRef.current.value) {
                    const query =
                        enteredFilter.length === 0
                            ? ''
                            : `?orderBy="title"&equalTo="${enteredFilter}"`;
                    fetch(
                        'https://react-hook-update-350d4-default-rtdb.firebaseio.com/ingredients.json' + query
                    )
                        .then(response => response.json())
                        .then(responseData => {
                            const loadedIngredients = [];
                            for (const key in responseData) {
                                loadedIngredients.push({
                                    id: key,
                                    title: responseData[key].title,
                                    amount: responseData[key].amount
                                });
                            }
                            onLoadIngredients(loadedIngredients);
                        });
                }
            }, 500);
            return () => {
                clearTimeout(timer);
            };
        }, [enteredFilter, onLoadIngredients, inputRef]);
        return(
            <section className="search">
                <Card>
                    <div className="search-input">
                        <label>搜索商品名稱(chēng)</label>
                        <input
                            ref={inputRef}
                            type="text"
                            value={enteredFilter}
                            onChange={event=>
    setEnterFilter(event.target.value)}
                        />
                    </div>
                </Card>
            </section>

        )

    });

    export  default Search;

    // components/Ingredients/Search.js

    上述代碼,我們定義為了避免頻繁觸發(fā)接口,定義了一個(gè)定時(shí)器,在用戶(hù)輸入500毫秒后在請(qǐng)求接口。你可以看到  useEffect() 里,我們使用了 return 方法,用于清理定時(shí)器,要不會(huì)有很多的定時(shí)器。同時(shí)依賴(lài)參數(shù)有三個(gè) [enteredFilter, onLoadIngredients,inputRef],只有用戶(hù)的輸入內(nèi)容和事件屬性發(fā)生變化時(shí),才會(huì)再次觸發(fā) useEffect() 中的邏輯。這里我們用到了useRef 方法獲取輸入框的值,關(guān)于其詳細(xì)的介紹,會(huì)在稍后的文章介紹。

    接下來(lái)貼上 Search.css 的相關(guān)代碼,由于內(nèi)容比較簡(jiǎn)單,這里就不過(guò)多解釋了。

    .search {
      width30rem;
      margin2rem auto;
      max-width80%;
    }

    .search-input {
      display: flex;
      justify-content: space-between;
      align-items: center;
      flex-direction: column;
    }

    .search-input input {
      font: inherit;
      border1px solid #ccc;
      border-radius5px;
      padding0.15rem 0.25rem;
    }

    .search-input input:focus {
      outline: none;
      border-color#ff2058;
    }

    @media (min-width: 768px) {
      .search-input {
        flex-direction: row;
      }
    }

    /* 
    components/Ingredients/Search.css
    */

    最后我們將 Search 組件添加至清單頁(yè)面,在這個(gè)頁(yè)面里定義了一個(gè) useCallback 的方法,類(lèi)似 Vue 的 computed 緩存的特性,避免重復(fù)計(jì)算,這個(gè)方法主要用來(lái)接收  Search 子組件傳輸數(shù)據(jù),用于更新 UserIngredients 數(shù)據(jù)中的狀態(tài),在稍后的文章里我會(huì)詳細(xì)介紹,這里只是簡(jiǎn)單的貼下代碼,示例代碼如下:

    const filteredIngredientsHandler = useCallback(filteredIngredients => {
            setUserIngredients(filteredIngredients)
        }, []);
    // components/Ingredients/Ingredients.js

    接下來(lái)在 return 里添加 Search 組件和 ErrorModal 組件,在 Search 組件的 ingredients 屬性里添加上述定義的 filteredIngredientsHandler 方法,用于接收組件搜索接口請(qǐng)求返回的數(shù)據(jù)內(nèi)容,用于更新 UserIngredients 的數(shù)據(jù)狀態(tài),示例代碼如下:

    <div className="App">
         {error && <ErrorModal onClose={clearError}>{error}</ErrorModal>}
         <IngredientForm onAddIngredient={addIngredientHandler} loading={isLoading}/>
                <section>
                    <Search onLoadIngredients={filteredIngredientsHandler}/>
                    <IngredientList ingredients={userIngredients} onRemoveItem={removeIngredientHandler}/>
                </section>
            </div>
    // components/Ingredients/Ingredients.js

    到這里,本節(jié)的實(shí)踐練習(xí)就完了,基本上是一個(gè)基于后端接口的,基礎(chǔ)的增刪改查案例,稍微完善下就可以運(yùn)用到你的實(shí)際案例中。你可以點(diǎn)擊閱讀原文進(jìn)行體驗(yàn)(主要本案例采用了Firebase ,科學(xué)上網(wǎng)才能在線體驗(yàn))。

    六、結(jié)束語(yǔ)

    好了,本篇關(guān)于 useEffect() 的介紹就結(jié)束了,希望你已經(jīng)理解了 useEffect 的基本用法,感謝你的閱讀,你可以點(diǎn)擊閱讀原文體驗(yàn)本文的案例部分,如果你想獲取源碼請(qǐng)回復(fù)"r2",小編建議親自動(dòng)手做一下,這樣才能加深對(duì) useEffect Hook 的認(rèn)知,下一篇本系列文章將會(huì)繼續(xù)介紹 useRef,敬請(qǐng)期待。

    瀏覽 61
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)
    評(píng)論
    圖片
    表情
    推薦
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    三级片在线播放网站 | 狠狠狠综合 | 欧美日韩一级黄片 | 久久加勒比婷婷 | 三级特黄毛片 |