1. 程式人生 > 其它 >封裝一個關於資料請求的hook

封裝一個關於資料請求的hook

原文:https://www.robinwieruch.de/react-hooks-fetch-data/

案例目錄

對應的hook

/**
 * 一個關於請求資料的hook,關鍵點:
 * 1. 資料初始化
 * 2. loading indicator
 * 3. error handling
 * 4. 用 useReducer 管理狀態
 * 5. 在元件被解除安裝後防止 state 改變
 */

import { useState, useEffect, useReducer } from "react";
import axios from "axios";

const useDataApi = (initialData, initalUrl) => {
  const [url, setUrl] = useState(initalUrl);
  const [state, dispatch] = useReducer(dataFetchReducer, {
    isLoading: false,
    isError: false,
    data: initialData
  });

  useEffect(() => {
    // 在頁面被解除安裝時,阻止元件state改變
    let didCancel = false;

    const fetchData = async () => {
      dispatch({ type: "FETCH_INIT" });

      try {
        const result = await axios(url);

        if (!didCancel)
          dispatch({ type: "FETCH_SUCCESS", payload: result.data });
      } catch (error) {
        if (!didCancel) dispatch({ type: "FETCH_FAILURE" });
      }
    };

    fetchData();

    return () => (didCancel = true);
  }, [url]);

  return [state, setUrl];
};

const dataFetchReducer = (state, action) => {
  switch (action.type) {
    case "FETCH_INIT":
      return {
        ...state,
        isLoading: true,
        isError: false
      };
    case "FETCH_SUCCESS":
      return {
        ...state,
        isLoading: false,
        isError: false,
        data: action.payload
      };
    case "FETCH_FAILURE":
      return {
        ...state,
        isLoading: false,
        isError: true
      };
    default:
      throw new Error();
  }
};

export default useDataApi;

App.jsx中的實踐

import React, { useState, Fragment } from "react";

import useDataApi from "./hooks/useHackerNewsApi";

import "./styles.css";

export default function App() {
  const [{ data, isLoading, isError }, doFetch] = useDataApi(
    { hits: [] },
    "https://hn.algolia.com/api/v1/search?query=redux"
  );
  const [query, setQuery] = useState("redux");

  return (
    <Fragment>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          doFetch(`https://hn.algolia.com/api/v1/search?query=${query}`);
        }}
      >
        <input
          type="text"
          value={query}
          onChange={(event) => setQuery(event.target.value)}
        />
        <button type="submit">Search</button>
      </form>

      {isError && <div>Something went wrong ...</div>}

      {isLoading ? (
        <div>Loading ...</div>
      ) : (
        <ul>
          {data.hits.map((item) => (
            <li key={item.objectID}>
              <a href={item.url}>{item.title}</a>
            </li>
          ))}
        </ul>
      )}
    </Fragment>
  );
}