本文使用 Star Wars API 為例示範 React 如何串接第三方 API。
範例:串接 Star Wars API
- API: Application Programming Interface
- SWAPI Film List
- Package axios
- Built-in Fetch API
- HTTP response status codes
使用 Fetch 透過網路取得 JSON,回傳的 response 需要先透過 json()
轉換,然後我們就能開始使用資料!
這邊有個小細節就是使用 map()
篩選出我們需要的欄位,不要把整包 API 資料都帶走,減少資料的複雜度。
function fetchMovieHandler() {
fetch("https://swapi.dev/api/films")
.then((res) => {
return res.json();
})
.then((data) => {
const transformedMovies = data.results.map((movieData) => {
return {
id: movieData.episode_id,
title: movieData.title,
openingText: movieData.opening_crawl,
releaseDate: movieData.release_date,
};
});
setMovies(transformedMovies);
})
.catch((err) => {
console.log(err);
});
}
我們也可以搭配 Async/Await 來使用,我本身也比較喜歡 async/await
大於 .then()
,因為讀起來更簡單、直覺。
注意,使用時除了在 Fetch 加上 await
之外,使用 json()
把回傳結果的 body text 解析成 JSON 型別的時候也要加上 await
。
async function fetchMovieHandler() {
const response = await fetch("https://swapi.dev/api/films");
const data = await response.json();
const transformedMovies = data.results.map((movieData) => {
return {
id: movieData.episode_id,
title: movieData.title,
openingText: movieData.opening_crawl,
releaseDate: movieData.release_date,
};
});
setMovies(transformedMovies);
}
Loading & Error Handling
最後是加上 Loading 與錯誤處理的部分,我們會用 Fetch API 作為範例,如果用的是其他 API 像是 axios,可能在寫法上會有些許差異。
function App() {
const [movies, setMovies] = useState([]);
const [isLoading, setIsLoading] = useState(false); // 是否正在讀取
const [error, setError] = useState(null); // 錯誤訊息
async function fetchMovieHandler() {
setIsLoading(true);
setError(null);
// 使用 try...catch 處理錯誤
try {
const response = await fetch("https://swapi.dev/api/films");
// 檢查 Fetch 回傳的狀態
if (!response.ok) {
throw new Error("Something went wrong!");
}
const data = await response.json();
const transformedMovies = data.results.map((movieData) => {
return {
id: movieData.episode_id,
title: movieData.title,
openingText: movieData.opening_crawl,
releaseDate: movieData.release_date,
};
});
setMovies(transformedMovies);
} catch (error) {
setError(error.message);
}
// 不論成功失敗最後都會關閉讀取
setIsLoading(false);
}
// 處理不同狀態下的呈現內容
let content = <p>Found no movies.</p>;
if (movies.length > 0) {
content = <MoviesList movies={movies} />;
}
if (error) {
content = <p>{error}</p>;
}
if (isLoading) {
content = <p>Loading...</p>;
}
return (
<React.Fragment>
<section>
<button onClick={fetchMovieHandler}>Fetch Movies</button>
</section>
<section>
{/* {!isLoading && movies.length > 0 && <MoviesList movies={movies} />}
{!isLoading && movies.length === 0 && !error && <p>Found no movies.</p>}
{isLoading && <p>Loading...</p>}
{!isLoading && error && <p>{error}</p>} */}
{content}
</section>
</React.Fragment>
);
}
export default App;
Working with useEffect and useCallback Hooks
最後我們用 useEffect 讓畫面渲染後先 Call API 獲取一次資料。
除此之外,我們會加上 useCallback 確保 fetchMovieHandler
函式不會在 useEffect 中形成無限迴圈,因此要在 dependency array 放入函式內有使用到的狀態。
(在這個範例中我們沒有使用任何依賴,但其他情況下就有可能會用到)
const fetchMovieHandler = useCallback(async () => {
// Do the same thing...
}, []);
useEffect(() => {
fetchMovieHandler();
}, [fetchMovieHandler]);
Sending a POST request to Firebase Realtime Database
Fetch API 除了 GET 之外也能用 POST,寫法是在 fetch()
的第二個參數放一個物件,然後基本上會設定 method
、body
,與 headers
這幾個基本的欄位。
async function addMovieHandler(movie) {
setError(null);
try {
console.log(movie);
const response = await fetch(
"https://react-http-14f5a-default-rtdb.firebaseio.com/movies.json",
{
method: "POST",
body: JSON.stringify(movie), // body want JSON data
// Firebase 不用設定 Content-Type,但一般保險起見還是都會設定
headers: {
"Content-Type": "application/json",
},
},
);
const data = await response.json();
console.log(data);
fetchMoviesHandler(); // Fetch movies after adding new movie
} catch (error) {
setError(error.message);
}
}
回顧
看完這篇文章,我們到底有什麼收穫呢?藉由本文可以理解到…
- Fetching API data with useEffect and useCallback Hooks