React logo

Custom Hooks โ€” ืžื“ืจื™ืš ืขื ื“ื•ื’ืžืื•ืช ื—ื™ื•ืช

ืžื” ื–ื” Custom Hook ื•ืœืžื” ืœื”ืฉืชืžืฉ?

Custom Hook ื”ื•ื ืคื•ื ืงืฆื™ื” ืฉืžืชื—ื™ืœื” ื‘-use ื•ืžืืคืฉืจืช ืœืื’ื“ ืœื•ื’ื™ืงืช Hooks ืœืฉื™ืžื•ืฉ ื—ื•ื–ืจ ื‘ื™ืŸ ืงื•ืžืคื•ื ื ื˜ื•ืช. ืœื“ื•ื’ืžื”: ื ืจืื•ืช/ื”ืกืชืจื” (toggle), ืฉืœื™ืคืช ื ืชื•ื ื™ื (fetch), ื“ื™ื‘ืื•ื ืก/ืชื–ืžื•ืŸ, ื ื™ื”ื•ืœ ืžืื–ื™ื ื™ื ื•ืขื•ื“ โ€” ืชื•ืš ืฉืžื™ืจื” ืขืœ ื—ื•ืงื™ Hooks ื•ืขืœ ืงื•ื“ ื ืงื™ ื•ืงืจื™ื.

ื—ื•ืงื™ ื–ื”ื‘

  • ื”-Hook ื—ื™ื™ื‘ ืœื”ืชื—ื™ืœ ื‘ืฉื use.
  • ืžื•ืชืจ ืœืงืจื•ื ืœ-Hooks ืจืง ื‘ืจืžื” ื”ืขืœื™ื•ื ื” ืฉืœ ื”-Hook (ืœื ื‘ืชื•ืš ืชื ืื™/ืœื•ืœืื”).
  • ื›ืœ ืชืœื•ืช ืฉืžื•ืคื™ืขื” ื‘ืœื•ื’ื™ืงืช ื”-Hook ื—ื™ื™ื‘ืช ืœื”ื•ืคื™ืข ื‘ืžืขืจื›ื™ ื”ืชืœื•ืชื™ื.
  • ืฉืžืจื• ืขืœ ืžืžืฉืง ืคืฉื•ื˜ ื•ื‘ืจื•ืจ: ื”ื—ื–ื™ืจื• ืื•ื‘ื™ื™ืงื˜/ื˜ื•ืคืœ ืงื˜ืŸ ืขื ืžื” ืฉื‘ืืžืช ืฆืจื™ืš.
  • ืคืจืงื• ืœื•ื’ื™ืงื•ืช: ืขื“ื™ืฃ ื›ืžื” Hooks ืงื˜ื ื™ื ืžืืฉืจ ืžืคืœืฆืช ืื—ืช.

ืชื•ื›ืŸ ืขื ื™ื™ื ื™ื

  1. useToggle โ€” ืžืชื’ ื ืจืื•ืช
  2. useFetch โ€” ืฉืœื™ืคืช ื ืชื•ื ื™ื ื›ืœืœื™ืช
  3. useFetchPerson โ€” ื•ืจื™ืืฆื™ื” ื™ื™ืขื•ื“ื™ืช
  4. ื˜ื™ืคื™ื, ืื ื˜ื™-ืคื˜ืจื ื™ื ื•ืžื‘ื ื” ืžื•ืžืœืฅ

1) useToggle โ€” ืžืชื’ ื ืจืื•ืช

Hook ืงื˜ืŸ ืœื ื™ื”ื•ืœ ื‘ื•ืœื™ืืŸ (ื ืจืื•ืช/ื”ืกืชืจื”). ืฉื™ืžื•ืฉื™ ืœื˜ื•ื’ืœื™ื, ืžื•ื“ืœื™ื, ืืงื•ืจื“ื™ื•ืŸ ื•ื›ื“'.

import { useState } from 'react';

function useToggle(defaultValue = false) {
  const [value, setValue] = useState(!!defaultValue);
  const toggle = () => setValue(v => !v);
  const on = () => setValue(true);
  const off = () => setValue(false);
  return { value, toggle, on, off };
}

export default useToggle;

ื“ื•ื’ืžื” ื—ื™ื”

toggle custom hook

some stuff

ืงื•ื“ ื”ื“ื•ื’ืžื”

import useToggle from './useToggle';

function ToggleExample() {
  const { value: show, toggle } = useToggle(true);
  return (
    <div>
      <h4>toggle custom hook</h4>
      <button className='btn' onClick={toggle}>toggle</button>
      {show && <h4>some stuff</h4>}
    </div>
  );
}

export default ToggleExample;

2) useFetch โ€” ืฉืœื™ืคืช ื ืชื•ื ื™ื ืขื Multiple Returns

Hook ื›ืœืœื™ ืฉืžื—ื–ื™ืจ isLoading, isError, data. ืžื˜ืคืœ ื‘-AbortController ื•ื ื™ืงื™ื•ืŸ, ื•ื‘ื ื•ื™ ืœืคื™ ืกื“ืจ Loading โ†’ Error โ†’ Success.

import { useState, useEffect } from 'react';

function useFetch(url, options = {}) {
  const [isLoading, setIsLoading] = useState(true);
  const [isError,   setIsError]   = useState(false);
  const [data,      setData]      = useState(null);

  useEffect(() => {
    if (!url) { setIsError(true); setIsLoading(false); return; }
    const ctrl = new AbortController();

    (async () => {
      try {
        const res = await fetch(url, { signal: ctrl.signal, ...options });
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        const json = await res.json();
        setData(json);
      } catch (err) {
        if (err.name !== 'AbortError') setIsError(true);
      } finally {
        setIsLoading(false);
      }
    })();

    return () => ctrl.abort();
  }, [url]); // ื‘ื›ื•ื•ื ื” ืชืœื•ื™ ืจืง ื‘-url, ืืคืฉืจ ืœื”ืจื—ื™ื‘ ืœ-options ืื ื™ืฆื™ื‘

  return { isLoading, isError, data };
}

export default useFetch;

ื“ื•ื’ืžื” ื—ื™ื” โ€” ืคืจื•ืคื™ืœ GitHub (QuincyLarson)

โŒ› Loadingโ€ฆ

ืงื•ื“ ื”ื“ื•ื’ืžื”

import useFetch from './useFetch';

const url = 'https://api.github.com/users/QuincyLarson';

function FetchData() {
  const { isLoading, isError, data: user } = useFetch(url);

  if (isLoading) return <h2>Loading...</h2>;
  if (isError)   return <h2>There was an error...</h2>;

  const { avatar_url, name, company, bio } = user || {};
  return (
    <div>
      <img style={{ width: 150, borderRadius: 25 }} src={avatar_url} alt={name} />
      <h2>{name}</h2>
      {company && <h4>works at {company}</h4>}
      <p>{bio}</p>
    </div>
  );
}

export default FetchData;
โš ๏ธ GitHub API ืžื•ื’ื‘ืœ ื‘ืงืฆื‘ ืงืจื™ืื•ืช ืœืœื ื˜ื•ืงืŸ. ืื ื ืชืงืœื™ื ื‘-rate limit, ืืคืฉืจ ืœื”ืขื‘ื™ืจ headers ืขื ื˜ื•ืงืŸ, ืื• ืœื”ื—ืœื™ืฃ ื›ืชื•ื‘ืช API.

3) useFetchPerson โ€” ื•ืจื™ืืฆื™ื” ืžืžื•ืงื“ืช ืœืื•ื‘ื™ื™ืงื˜ "ืžืฉืชืžืฉ"

ืื•ืชื” ืœื•ื’ื™ืงื”, ืืš ืขื state ืžืžื•ืงื“ ื‘ืฉื user ืœื”ื‘ื”ืจืช ื”ื›ื•ื•ื ื” ื‘ืงื•ืžืคื•ื ื ื˜ื” ื”ืงื•ืจืืช.

import { useState, useEffect } from 'react';

function useFetchPerson(url) {
  const [isLoading, setIsLoading] = useState(true);
  const [isError,   setIsError]   = useState(false);
  const [user,      setUser]      = useState(null);

  useEffect(() => {
    if (!url) { setIsError(true); setIsLoading(false); return; }
    const ctrl = new AbortController();

    (async () => {
      try {
        const res = await fetch(url, { signal: ctrl.signal });
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        const json = await res.json();
        setUser(json);
      } catch (err) {
        if (err.name !== 'AbortError') setIsError(true);
      } finally {
        setIsLoading(false);
      }
    })();

    return () => ctrl.abort();
  }, [url]);

  return { isLoading, isError, user };
}

export default useFetchPerson;

ื“ื•ื’ืžื” ื—ื™ื” โ€” ืื•ืชื• ื”ื™ื•ื–ืจ ืขื Hook ื™ื™ืขื•ื“ื™

โŒ› Loadingโ€ฆ

ื˜ื™ืคื™ื ื•ื“ื’ืฉื™ื

  • ื”ื•ืฆื™ืื• ืžื”-Hook ืจืง ืžื” ืฉื‘ืืžืช ืฆืจื™ืš โ€” ืฉืžืจื• ืขืœ API ืงื˜ืŸ.
  • ืืœ ืชื—ืฉืคื• setState ื’ื•ืœืžื™ ืื ืื™ืŸ ืฆื•ืจืš โ€” ื”ื—ื–ื™ืจื• ืคืขื•ืœื•ืช (ื›ืžื• toggle/on/off).
  • ืœ-fetch: ืฆืจืคื• ื ื™ืงื•ื™ ืขื AbortController, ื•ื˜ืคืœื• ื‘-!res.ok.
  • ื”ืชืื™ืžื• ืืช ืžืขืจื›ื™ ื”ืชืœื•ืชื™ื ื›ืš ืฉื”-Hook ื™ื”ื™ื” ืฆืคื•ื™ ื•ืœื ื™ืคืชื™ืข ื‘ืจื™ืฆื•ืช ืžื™ื•ืชืจื•ืช.
  • ืฉืงืœื• ืคื™ืฆื•ืœ ืœ-Hooks ืงื˜ื ื™ื ื ืคืจื“ื™ื ื‘ืžืงื•ื Hook ืขื ืง ืขื ื”ืจื‘ื” ืžืฆื‘ื™ื.