ReactJS - 使用useEffect



React 提供了useEffect 用於在元件中執行副作用。一些副作用如下:

  • 從外部來源獲取資料並更新渲染內容。

  • 渲染後更新DOM元素。

  • 訂閱

  • 使用計時器

  • 日誌記錄

在基於類的元件中,這些副作用是使用生命週期元件完成的。因此,useEffect hook 是以下生命週期事件的替代方案。

  • componentDidMount - 首次渲染完成後觸發。

  • componentDidUpdate - 由於屬性或狀態更改導致渲染更新後觸發。

  • componentWillUnmount - 元件銷燬期間解除安裝渲染內容後觸發。

讓我們在本節學習如何在元件中使用effect hook。

useEffect 的簽名

useEffect 的簽名如下:

useEffect( <update function>, <dependency> )

其中,更新函式的簽名如下:

{
   // code
   return <clean up function>
}

這裡:

更新函式 - 更新函式是在每個渲染階段後執行的函式。這對應於componentDidMountcomponentDidUpdate 事件。

依賴項 - 依賴項是一個數組,其中包含函式所依賴的所有變數。指定依賴項對於最佳化 effect hook 至關重要。通常,更新函式在每次渲染後都會被呼叫。有時不需要在每次渲染時都執行更新函式。讓我們考慮一下,我們正在從外部來源獲取資料並在渲染階段之後更新它,如下所示:

const [data, setDate] = useState({})
const [toggle, setToggle] = useState(false)
const [id, setID] = useState(0)
useEffect( () => {
   fetch('/data/url/', {id: id}).then( fetchedData => setData(fetchedData) )
})
// code

每當datatoggle 變數更新時,元件都會重新渲染。但是,正如您所看到的,我們不需要在每次更新toggle狀態時都執行定義的effect。為了解決這個問題,我們可以傳遞一個空依賴項,如下所示:

const [data, setDate] = useState({})
const [toggle, setToggle] = useState(false)
const [id, setID] = useState(0)
useEffect( () => {
   fetch('/data/url/', { id: id }).then( fetchedData => setData(fetchedData) )
}, [])

上面的程式碼只會在第一次渲染後執行一次 effect。儘管它會修復問題,但 effect 必須在每次id更改時執行。為了實現這一點,我們可以將id作為 effect 的依賴項包含在內,如下所示:

const [data, setDate] = useState({})
const [toggle, setToggle] = useState(false)
const [id, setID] = useState(0)
useEffect( () => {
   fetch('/data/url/', { id: id }).then( fetchedData => setData(fetchedData) )
}, [id])

這將確保 effect 只會在id修改後重新執行。

清理函式 - 清理函式用於在使用訂閱函式和計時器函式時進行清理工作,如下所示:

const [time, setTime] = useState(new Date())
useEffect(() => {
   let interval = setInterval(() => {
      setTime(new Date())
   }, 1000)
   return () => clearInterval(interval)
}, [time])

讓我們在後面的章節中建立一個完整的應用程式來理解清理函式。

effect hook 的特性

effect hook 的一些顯著特性如下:

  • React 允許在函式元件中使用多個 effect hook。這將幫助我們為每個副作用編寫一個函式,並將其設定為單獨的 effect。

  • 每個 hook 將按照宣告的順序執行。開發者應確保 effect 的宣告順序正確。

  • 依賴項特性可用於提高副作用的效能和正確性。

  • 清理函式可以防止記憶體洩漏和不必要的事件觸發。

使用 effect 獲取資料

在本節中,讓我們建立一個應用程式,該應用程式將從外部來源獲取資料並使用useEffect hook 渲染它。

首先,建立一個新的React應用程式,並使用以下命令啟動它。

create-react-app myapp
cd myapp
npm start

接下來,在元件資料夾下建立一個React元件NameList(src/components/NameList.js)

function NameList() {
   return <div>names</div>
}
export default NameList

這裡,NameList元件的目的是展示流行的常見名字列表。

接下來,更新根元件App.js以使用新建立的NameList元件。

import NameList from "./components/NameList";
function App() {
   return (
      <div style={{ padding: "5px"}}>
         <NameList />
      </div>
   );
}
export default App;

接下來,建立一個json檔案names.json(public/json/names.json),並以json格式儲存流行的名稱,如下所示。

[
   {
      "id": 1,
      "name": "Liam"
   },
   {
      "id": 2,
      "name": "Olivia"
   },
   {
      "id": 3,
      "name": "Noah"
   },
   {
      "id": 4,
      "name": "Emma"
   },
   {
      "id": 5,
      "name": "Oliver"
   },
   {
      "id": 6,
      "name": "Charlotte"
   },
   {
      "id": 7,
      "name": "Elijah"
   },
   {
      "id": 8,
      "name": "Amelia"
   },
   {
      "id": 9,
      "name": "James"
   },
   {
      "id": 10,
      "name": "Ava"
   },
   {
      "id": 11,
      "name": "William"
   },
   {
      "id": 12,
      "name": "Sophia"
   },
   {
      "id": 13,
      "name": "Benjamin"
   },
   {
      "id": 14,
      "name": "Isabella"
   },
   {
      "id": 15,
      "name": "Lucas"
   },
   {
      "id": 16,
      "name": "Mia"
   },
   {
      "id": 17,
      "name": "Henry"
   },
   {
      "id": 18,
      "name": "Evelyn"
   },
   {
      "id": 19,
      "name": "Theodore"
   },
   {
      "id": 20,
      "name": "Harper"
   }
]

接下來,建立一個新的狀態變數data,用於在NameList元件中儲存流行的名稱,如下所示:

const [data, setData] = useState([])

接下來,建立一個新的狀態變數isLoading來儲存載入狀態,如下所示:

const [isLoading, setLoading] = useState([])

接下來,使用fetch方法從json檔案獲取流行的名稱,並將其設定到useEffect hook內的data狀態變數中。

useEffect(() => {
   setTimeout(() => {
      fetch("json/names.json")
         .then( (response) => response.json())
         .then( (json) => { console.log(json); setLoading(false); setData(json); } )
   }, 2000)
})

這裡我們有:

  • 使用setTimout方法來模擬載入過程。

  • 使用fetch方法獲取json檔案。

  • 使用json方法解析json檔案。

  • 使用setData將從json檔案中解析的名稱設定為data狀態變數。

  • 使用setLoading設定載入狀態。

接下來,使用map方法渲染名稱。在獲取過程中,顯示載入狀態。

<div>
   {isLoading && <span>loading...</span>}
   {!isLoading && data && <span>Popular names: </span>}
   {!isLoading && data && data.map((item) =>
      <span key={item.id}>{item.name} </span>
   )}
</div>

這裡我們有:

  • 使用isLoading顯示載入狀態。

  • 使用data變數顯示流行名稱列表。

NameList元件的完整原始碼如下:

import { useState, useEffect } from "react"
function NameList() {
   const [data, setData] = useState([])
   const [isLoading, setLoading] = useState([])
   useEffect(() => {
      setTimeout(() => {
         fetch("json/names.json")
         .then( (response) => response.json())
         .then( (json) => { console.log(json); setLoading(false); setData(json); } )
      }, 2000)
   })
   return (
      <div>
         {isLoading && <span>loading...</span>}
         {!isLoading && data && <span>Popular names: </span>}
         {!isLoading && data && data.map((item) =>
            <span key={item.id}>{item.name} </span>
         )}
      </div>
   )
}
export default NameList

接下來,開啟瀏覽器並檢查應用程式。它將顯示載入狀態,2秒後,它將獲取json並顯示流行的名稱,如下所示:

Fetching Data using Effect

Fetching Data using Effect

DOM操作

useEffect hook 可用於使用DOM及其方法操作文件。它確保其中的程式碼只在DOM準備就緒後執行。讓我們更改我們的名稱列表應用程式並使用DOM操作更新頁面的標題。

首先,開啟NameList元件,並根據載入狀態新增文件標題,如下所示:

useEffect(() => {
   if(isLoading)
      document.title = "Loading popular names..."
   else
      document.title = "Popular name list"
   setTimeout(() => {
      fetch("json/names.json")
         .then( (response) => response.json())
         .then( (json) => { console.log(json); setLoading(false); setData(json);} )
   }, 2000)
})

在這裡,我們使用了DOM物件document.title來更新頁面的標題。

最後,開啟瀏覽器並檢查文件標題如何透過DOM操作更新。

DOM Mutations

DOM Mutations

清理函式

useEffect 可用於在元件從頁面文件解除安裝期間刪除清理函式,例如clearIntervalremoveEventListener等。這將防止記憶體洩漏並提高效能。為此,我們可以建立我們自己的清理函式並將其從useEffect回撥引數返回。

讓我們更改我們的名稱列表應用程式以使用setInterval代替setTimeout,然後使用clearInterval在解除安裝元件期間刪除設定的回撥函式。

首先,開啟NameList元件並更新useEffect部分,如下所示:

useEffect(() => {
   if(isLoading)
      document.title = "Loading popular names..."
   else
      document.title = "Popular name list"
   let interval = setInterval(() => {
      setLoading(true)
      fetch("json/names.json")
         .then( (response) => response.json())
         .then( (json) => { console.log(json); setLoading(false); setData(json);} )
      }, 5000)
   return () => { clearInterval(interval) }
})

這裡我們有:

  • 使用setImterval每5秒更新一次流行名稱。

  • 在清理函式中使用clearInterval在解除安裝元件期間刪除setInterval。

最後,開啟瀏覽器並檢查應用程式的行為。我們將看到資料每5秒更新一次。當元件解除安裝時,clearInterval將在後臺呼叫。

總結

useEffect是函式元件的重要特性,使元件能夠使用生命週期事件。它幫助函式元件提供具有可預測性和最佳化效能的豐富功能。

廣告