0%

React useEffect() 使用情境與呼叫 API

useEffect() 本身是個函式,由 React 框架提供。

什麼時候使用 useEffect()

當元件狀態(useState)或變數變動之後,要做什麼事情可透過useEffect來監聽執行。
useEffect() 也是個通用的 Hook,找不到對應的 Hook 使用時也可以使用 useEffect()

例如當我們載某個元件載入之後,希望網頁標題隨之變化顯示:「歡迎來這裡」,那麼改變網頁標題這個動作就需要透過 useEffect 來完成。

1
2
3
4
5
6
7
8
9
10
11
import React, { useEffect } from "react";
import ReactDOM from "react-dom";

const Welcome=(props)=>{
useEffect(() => {
document.title = '歡迎來這裡'
})
return <h1>Hello, {props.name}</h1>
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Welcome name="Tracy" />, rootElement);

useEffect() 做什麼事?

以上述的例子來說,useEffect() 的參數是函式,這個函式要完成的就是改變網頁的標題,元件載入後 React 就會去執行這個參數的函式。元件每渲染一次,此函式就自動執行一次,元件在第一次載入頁面時,這個函式也會執行。

useEffect() 的第二個參數

剛剛說過 useEffect()的第一個參數是函式,第二個參數何時用呢?
有時候我們並不希望 useEffect() 每次渲染都執行,這時第二參數就派上用場,使用一個 Array 來指定 useEffect() 的依賴項(資料),只有依賴項發生變化,才會重新渲染。

1
2
3
4
5
6
const Welcome=(props)=>{
useEffect(() => {
document.title = `hello, ${props.name}`
},[props.name])
return <h1>Hello, {props.name}</h1>
}

上述例子中,useEffect()的第二個參數是 Array,指定第一個參數函式的依賴項(props.name)只有此依賴項的變數產生變化時,第一參數的函式才會執行。
如果第二參數是一個空的 Array,就表示第一個參數函式沒有依賴項,也因此第個參數函式只會在元件載入頁面時執行一次,之後元件重新渲染時就不會執行了。

useEffect() 的用途

useEffect() 常見的用途有:

  • 獲取資料/call API (data fetching)
  • 事件的監聽或訂閱(setting up a subscription)
  • 改變 DOM 元素(changing the DOM)
  • 輸出日誌(logging)

使用 useEffect call API

終於寫到重點了,useEffect()最常用來獲取遠端伺服器上的資料(call API),以下是範例:
此範例使用 axios 套件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import React, {useState, useEffect} from 'react'
import axios from 'axios'

const urlAPI = 'https://hn.algolia.com/api/v1/search'

const GetHitList = () => {
const [data, setData] = useState({hits:[]}) // hit: 轟動一時的人的列表

useEffect(()=>{
const fetchData = async () => {
const result = await axios(`${urlAPI}?query=redux`)

setDate(result.data)
}

fetchData()
},[])
return (
<ul>
{data.hits.map((item) => {
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
})}
</ul>
)
}

export default GetHitList
  • useState()來產生一個狀態變數data來保存取的得的資料;
  • useEffect()的第一參數函式使用 async 用來取得從遠端伺服器上非同步取得的資料;
  • 取得資料後再用setData()來觸發元件的重新渲染;
  • 因為取得資料只需要一次,所以useState()的第二參數依賴項為空的 Array;

其他 Call API 的寫法

以下範例是以useEffect()呼叫 API ,並把回傳的圖片 url 顯示到畫面上的三種寫法,都是使用fetch()

  • fetch()是XMLHttpRequest 的升級版,用於在 JavaScript 裡發出 HTTP 請求時使用。
  • fetch()使用Promise,不使用 callback 函式,因此大大簡化了寫法,寫起來更簡潔。

第一種寫法 fetch, async / await

1
2
3
4
5
6
7
8
9
10
11
12
13
function CallApi() {
const [data, setData] = React.useState({})
const urlAPI = 'https://dog.ceo/api/breeds/image/random'
// 第一種寫法 async / await 1.
React.useEffect(()=>{
// 先把 call API 的邏輯放到一個單獨的函式中,再呼叫它
const fetchAPI = async () => {
const res = await fetch(urlAPI)
const resJson = await res.json()
setData(resJson)
}
fetchAPI()
}, [])

第二種寫法 fetch, async / await + try / catch

1
2
3
4
5
6
7
8
9
10
11
12
React.useEffect(()=>{
const getData = async () => {
try {
const res = await fetch(urlAPI)
const resJson = await res.json()
return setData(resJson)
} catch (err) {
console.log(err)
}
}
getData()
}, [])

第二種寫法 fetch, then / catch

1
2
3
4
5
6
7
8
9
10
11
12
React.useEffect(()=>{
fetch(urlAPI)
.then(res => res.json())
.then(resJson => setData(resJson))
.catch(err => console.log(err))
}, [])

return <div>
{
data.message && <img src={data.message} />
}
</div>

useEffect() 何時需要回傳值?

useEffect()是隨著元件載入的時候執行的,那麼在元件卸載時,有時也需要清理這些useEffect()useEffect()可以回傳一個函式,在元件卸載時執行這個函式就可以清除。
如果不需要清除useEffect()第一參數函式所做的事,就可以不用回傳任何值。
例如,在元件載入時,我們使用useEffect()在加載時訂閱了一個事件,並希望在元件卸載時取消訂閱,這時就可以用useEffect()回傳的函式去取消。

1
2
3
4
5
6
useEffect(() => {
const subscription = props.source.subscribe()
return () => {
subscription.unsubscribe()
}
},[props,source])

使用 useEffect() 的注意事項

  • 一次只做一件事,不要在useEffect()裡一次做多件事,例如 TodoList 有讀取、寫入、刪除,這三件事需要寫成三個useEffect()