React 中,取得資料並呈現到畫面上一直是個難題,useEffect 就是讓元件呈現【副作用 (Side Effects) 效果】一樣,對我們造成了很大的【副作用】的【效果】。
useEffect 要解決什麼樣的問題?
useEffect 主要在呈現 (Render) 後觸發此 Hook 事件,並將取得的資料後再呈現到元件上。
在【How to fetch data with React Hook?】一文中卻提到:
Note: In the future, React Hooks are not be intended for data fetching in React. Instead, a feature called Suspense will be in charge for it.這句話中文大概的意思是:
未來 React Hooks 不會用在資料取得的場合,會改由 Suspense 取代。
可是 Suspense 也說明它不會取代 fetch、axios 等取得資料的 API,在目前有限的資料只知道未來還會變化,所以現階段學習 useEffect 是必須的,待情勢明朗後再來調整也來得及。
取得資料的基本應用 - 一切的起點
以下範例源自【How to fetch data with React Hooks?】:
import React, { useState } from 'react';
function App() {
const [data, setData] = useState({ hits: [] });
return (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
export default App;
這個應用程式為【黑客新聞】,畫面裡有一清單。useState 為此清單的內部狀態,為取得新聞資料存在,預設內容為帶有 hits 屬性的空陣列物件;陣例裡的物件會有【objectID】、【url】和【title】屬性。 ( Step1 CodeSandbox )
取得資料的基本應用 - 引入 axios 函式庫
取得 WebAPI 資料時,我們會考慮 Promise fetch,另一個選擇是封裝 fetch 的【axios】函式庫,你會發現,axios 更好用。
接下來我們要使用【作用掛勾 (effect hook)】來看看 axios 取得資料後為 App 物件進行什麼樣的副作用:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
const [data, setData] = useState({ hits: [] });
useEffect(async () => {
const result = await axios(
'https://hn.algolia.com/api/v1/search?query=redux',
);
setData(result.data);
});
return (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
export default App;
然後就出錯了。
我在 CodeSandbox 輸入上述內容後會得到【邊界錯誤】。
因為 useEffect 在引數內使用 async 是錯誤的,它不能在引數內回傳 Promise 物件。
useEffect 內容要保持簡單,Promise 要包裝在 useEffect 內做一個 Closure function,所以要調整如下:
import React, { useState, useEffect } from "react";
import axios from "axios";
import "./styles.css";
export default function App() {
const [data, setData] = useState({ hits: [] });
useEffect(() => {
async function fetchData() {
const result = await axios(
"https://hn.algolia.com/api/v1/search?query=redux",
);
setData(result.data);
}
console.log("run useEffect")
fetchData()
});
return (
<ul>
{data.hits.map((item) => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
完成修改後,專案就不會有警告和錯誤, 但卻會不停的向 WebAPI 取資料,達成 DDOS 的目標。
這是因為 setData 觸發狀態改變,而進行繪製 (Render) 事件,而繪製後又會再進行【作用】處理,作用中向 WebAPI 拉資料後產生副作用,副作用又觸發狀態改變,如此無限循環,這是錯誤的。
此時,就需要用到 useEffect 的第二個參數:依賴 dependency。
只有在【初次繪製】和【依賴變更】時,元件才需要產生副作用,除此之外,就不要再繪製了。
以這個案例來說,並不用依賴清單 (data) 異動來進行繪製,案例中也沒有需要依賴產生副作用的 props 或 state,所以給予空陣列【[]】即可。
最終會得到以下的程式:【Step2 CodeSandbox】
import React, { useState, useEffect } from "react";
import axios from "axios";
import "./styles.css";
export default function App() {
const [data, setData] = useState({ hits: [] });
useEffect(() => {
async function fetchData() {
const result = await axios(
"https://hn.algolia.com/api/v1/search?query=redux"
);
setData(result.data);
}
console.log("hi")
fetchData();
}, []);
return (
<ul>
{data.hits.map((item) => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
useEffect 最基礎的內容就先介紹到這裡。
目標是 useEffect 內如何重用自己寫的下載函式,我們下次見!
See also
沒有留言:
張貼留言