Sean's Blog

An image showing avatar

Hi, I'm Sean

這裡記錄我學習網站開發的筆記
歡迎交流 (ゝ∀・)b

LinkedInGitHub

Sending Http Requests feat. Star Wars API

本文使用 Star Wars API 為例示範 React 如何串接第三方 API。

範例:串接 Star Wars API

使用 Fetch 透過網路取得 JSON,回傳的 response 需要先透過 json() 轉換,然後我們就能開始使用資料!

這邊有個小細節就是使用 map() 篩選出我們需要的欄位,不要把整包 API 資料都帶走,減少資料的複雜度。

1function fetchMovieHandler() {
2  fetch('https://swapi.dev/api/films')
3    .then((res) => {
4      return res.json();
5    })
6    .then((data) => {
7      const transformedMovies = data.results.map((movieData) => {
8        return {
9          id: movieData.episode_id,
10          title: movieData.title,
11          openingText: movieData.opening_crawl,
12          releaseDate: movieData.release_date,
13        };
14      });
15      setMovies(transformedMovies);
16    })
17    .catch((err) => {
18      console.log(err);
19    });
20}

我們也可以搭配 Async/Await 來使用,我本身也比較喜歡 async/await 大於 .then(),因為讀起來更簡單、直覺。

注意,使用時除了在 Fetch 加上 await 之外,使用 json() 把回傳結果的 body text 解析成 JSON 型別的時候也要加上 await

1async function fetchMovieHandler() {
2  const response = await fetch('https://swapi.dev/api/films');
3  const data = await response.json();
4
5  const transformedMovies = data.results.map((movieData) => {
6    return {
7      id: movieData.episode_id,
8      title: movieData.title,
9      openingText: movieData.opening_crawl,
10      releaseDate: movieData.release_date,
11    };
12  });
13  setMovies(transformedMovies);
14}

Loading & Error Handling

最後是加上 Loading 與錯誤處理的部分,我們會用 Fetch API 作為範例,如果用的是其他 API 像是 axios,可能在寫法上會有些許差異。

1function App() {
2  const [movies, setMovies] = useState([]);
3  const [isLoading, setIsLoading] = useState(false); // 是否正在讀取
4  const [error, setError] = useState(null); // 錯誤訊息
5
6  async function fetchMovieHandler() {
7    setIsLoading(true);
8    setError(null);
9
10    // 使用 try...catch 處理錯誤
11    try {
12      const response = await fetch('https://swapi.dev/api/films');
13      // 檢查 Fetch 回傳的狀態
14      if (!response.ok) {
15        throw new Error('Something went wrong!');
16      }
17      const data = await response.json();
18      const transformedMovies = data.results.map((movieData) => {
19        return {
20          id: movieData.episode_id,
21          title: movieData.title,
22          openingText: movieData.opening_crawl,
23          releaseDate: movieData.release_date,
24        };
25      });
26      setMovies(transformedMovies);
27    } catch (error) {
28      setError(error.message);
29    }
30
31    // 不論成功失敗最後都會關閉讀取
32    setIsLoading(false);
33  }
34
35  // 處理不同狀態下的呈現內容
36  let content = <p>Found no movies.</p>;
37  if (movies.length > 0) {
38    content = <MoviesList movies={movies} />;
39  }
40  if (error) {
41    content = <p>{error}</p>;
42  }
43  if (isLoading) {
44    content = <p>Loading...</p>;
45  }
46
47  return (
48    <React.Fragment>
49      <section>
50        <button onClick={fetchMovieHandler}>Fetch Movies</button>
51      </section>
52      <section>
53        {/* {!isLoading && movies.length > 0 && <MoviesList movies={movies} />}
54        {!isLoading && movies.length === 0 && !error && <p>Found no movies.</p>}
55        {isLoading && <p>Loading...</p>}
56        {!isLoading && error && <p>{error}</p>} */}
57        {content}
58      </section>
59    </React.Fragment>
60  );
61}
62
63export default App;

Working with useEffect and useCallback Hooks

最後我們用 useEffect 讓畫面渲染後先 Call API 獲取一次資料。

除此之外,我們會加上 useCallback 確保 fetchMovieHandler 函式不會在 useEffect 中形成無限迴圈,因此要在 dependency array 放入函式內有使用到的狀態。

(在這個範例中我們沒有使用任何依賴,但其他情況下就有可能會用到)

1const fetchMovieHandler = useCallback(async () => {
2  // Do the same thing...
3}, []);
4
5useEffect(() => {
6  fetchMovieHandler();
7}, [fetchMovieHandler]);

Sending a POST request to Firebase Realtime Database

Fetch API 除了 GET 之外也能用 POST,寫法是在 fetch() 的第二個參數放一個物件,然後基本上會設定 methodbody,與 headers 這幾個基本的欄位。

1async function addMovieHandler(movie) {
2  setError(null);
3
4  try {
5    console.log(movie);
6    const response = await fetch(
7      'https://react-http-14f5a-default-rtdb.firebaseio.com/movies.json',
8      {
9        method: 'POST',
10        body: JSON.stringify(movie), // body want JSON data
11        // Firebase 不用設定 Content-Type,但一般保險起見還是都會設定
12        headers: {
13          'Content-Type': 'application/json',
14        },
15      }
16    );
17    const data = await response.json();
18    console.log(data);
19
20    fetchMoviesHandler(); // Fetch movies after adding new movie
21  } catch (error) {
22    setError(error.message);
23  }
24}

回顧

看完這篇文章,我們到底有什麼收穫呢?藉由本文可以理解到…

  • Fetching API data with useEffect and useCallback Hooks

References