封裝一個關於資料請求的hook
阿新 • • 發佈:2022-05-30
原文: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> ); }