Svenska

Lär dig att använda React custom hooks för att extrahera och återanvända komponentlogik, vilket förbättrar kodens underhållbarhet, testbarhet och applikationens arkitektur.

React Custom Hooks: Extrahera komponentlogik för återanvändbarhet

React hooks har revolutionerat sättet vi skriver React-komponenter på, genom att erbjuda ett mer elegant och effektivt sätt att hantera tillstånd och sidoeffekter. Bland de olika hooks som finns tillgängliga sticker custom hooks ut som ett kraftfullt verktyg för att extrahera och återanvända komponentlogik. Den här artikeln ger en omfattande guide för att förstå och implementera React custom hooks, vilket ger dig möjlighet att bygga mer underhållbara, testbara och skalbara applikationer.

Vad är React Custom Hooks?

I huvudsak är en custom hook en JavaScript-funktion vars namn börjar med "use" och som kan anropa andra hooks. Den låter dig extrahera komponentlogik till återanvändbara funktioner, vilket eliminerar kodduplicering och främjar en renare komponentstruktur. Till skillnad från vanliga React-komponenter renderar custom hooks ingen UI; de kapslar bara in logik.

Tänk på dem som återanvändbara funktioner som kan komma åt React-tillstånd och livscykelfunktioner. De är ett fantastiskt sätt att dela tillståndsbaserad logik mellan olika komponenter utan att ta till högre-ordningskomponenter eller render props, vilket ofta kan leda till kod som är svår att läsa och underhålla.

Varför använda Custom Hooks?

Fördelarna med att använda custom hooks är många:

Skapa din första Custom Hook

Låt oss illustrera skapandet och användningen av en custom hook med ett praktiskt exempel: hämtning av data från ett API.

Exempel: useFetch - En datahämtnings-hook

Föreställ dig att du ofta behöver hämta data från olika API:er i din React-applikation. Istället för att upprepa hämtningslogiken i varje komponent kan du skapa en useFetch-hook.


import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const abortController = new AbortController();
    const signal = abortController.signal;

    const fetchData = async () => {
      setLoading(true);
      try {
        const response = await fetch(url, { signal: signal });
        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`);
        }
        const json = await response.json();
        setData(json);
        setError(null); // Clear any previous errors
      } catch (error) {
        if (error.name === 'AbortError') {
          console.log('Fetch aborted');
        } else {
          setError(error);
        }
        setData(null); // Clear any previous data
      } finally {
        setLoading(false);
      }
    };

    fetchData();

    return () => {
      abortController.abort(); // Cleanup function to abort the fetch on unmount or URL change
    };
  }, [url]); // Re-run effect when the URL changes

  return { data, loading, error };
}

export default useFetch;

Förklaring:

Använda useFetch-hooken i en komponent

Låt oss nu se hur man använder denna custom hook i en React-komponent:


import React from 'react';
import useFetch from './useFetch';

function UserList() {
  const { data: users, loading, error } = useFetch('https://jsonplaceholder.typicode.com/users');

  if (loading) return <p>Loading users...</p>;
  if (error) return <p>Error: {error.message}</p>;
  if (!users) return <p>No users found.</p>;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name} ({user.email})</li>
      ))}
    </ul>
  );
}

export default UserList;

Förklaring:

Avancerade Custom Hook-mönster

Utöver enkel datahämtning kan custom hooks användas för att kapsla in mer komplex logik. Här är några avancerade mönster:

1. Tillståndshantering med useReducer

För mer komplexa tillståndshanteringsscenarier kan du kombinera custom hooks med useReducer. Detta gör att du kan hantera tillståndsövergångar på ett mer förutsägbart och organiserat sätt.


import { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function useCounter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  const increment = () => dispatch({ type: 'increment' });
  const decrement = () => dispatch({ type: 'decrement' });

  return { count: state.count, increment, decrement };
}

export default useCounter;

Användning:


import React from 'react';
import useCounter from './useCounter';

function Counter() {
  const { count, increment, decrement } = useCounter();

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
}

export default Counter;

2. Kontex-integration med useContext

Custom hooks kan också användas för att förenkla åtkomsten till React Context. Istället för att använda useContext direkt i dina komponenter kan du skapa en custom hook som kapslar in logiken för kontex-åtkomst.


import { useContext } from 'react';
import { ThemeContext } from './ThemeContext'; // Assuming you have a ThemeContext

function useTheme() {
  return useContext(ThemeContext);
}

export default useTheme;

Användning:


import React from 'react';
import useTheme from './useTheme';

function MyComponent() {
  const { theme, toggleTheme } = useTheme();

  return (
    <div style={{ backgroundColor: theme.background, color: theme.color }}>
      <p>This is my component.</p>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
}

export default MyComponent;

3. Debouncing och Throttling

Debouncing och throttling är tekniker som används för att kontrollera hastigheten med vilken en funktion exekveras. Custom hooks kan användas för att kapsla in denna logik, vilket gör det enkelt att tillämpa dessa tekniker på händelsehanterare.


import { useState, useEffect, useRef } from 'react';

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return debouncedValue;
}

export default useDebounce;

Användning:


import React, { useState } from 'react';
import useDebounce from './useDebounce';

function SearchInput() {
  const [searchValue, setSearchValue] = useState('');
  const debouncedSearchValue = useDebounce(searchValue, 500); // Debounce for 500ms

  useEffect(() => {
    // Perform search with debouncedSearchValue
    console.log('Searching for:', debouncedSearchValue);
    // Replace console.log with your actual search logic
  }, [debouncedSearchValue]);

  const handleChange = (event) => {
    setSearchValue(event.target.value);
  };

  return (
    <input
      type="text"
      value={searchValue}
      onChange={handleChange}
      placeholder="Search..."
    />
  );
}

export default SearchInput;

Bästa praxis för att skriva Custom Hooks

För att säkerställa att dina custom hooks är effektiva och underhållbara, följ dessa bästa praxis:

Globala överväganden

När du utvecklar applikationer för en global publik, tänk på följande:

Exempel: Internationaliserad datumformatering med en Custom Hook


import { useState, useEffect } from 'react';
import { DateTimeFormat } from 'intl';

function useFormattedDate(date, locale) {
  const [formattedDate, setFormattedDate] = useState('');

  useEffect(() => {
    try {
      const formatter = new DateTimeFormat(locale, {
        year: 'numeric',
        month: 'long',
        day: 'numeric',
      });
      setFormattedDate(formatter.format(date));
    } catch (error) {
      console.error('Error formatting date:', error);
      setFormattedDate('Invalid Date');
    }
  }, [date, locale]);

  return formattedDate;
}

export default useFormattedDate;

Användning:


import React from 'react';
import useFormattedDate from './useFormattedDate';

function MyComponent() {
  const today = new Date();
  const enDate = useFormattedDate(today, 'en-US');
  const frDate = useFormattedDate(today, 'fr-FR');
  const deDate = useFormattedDate(today, 'de-DE');

  return (
    <div>
      <p>US Date: {enDate}</p>
      <p>French Date: {frDate}</p>
      <p>German Date: {deDate}</p>
    </div>
  );
}

export default MyComponent;

Slutsats

React custom hooks är en kraftfull mekanism för att extrahera och återanvända komponentlogik. Genom att utnyttja custom hooks kan du skriva renare, mer underhållbar och testbar kod. När du blir mer skicklig med React kommer att bemästra custom hooks avsevärt att förbättra din förmåga att bygga komplexa och skalbara applikationer. Kom ihåg att följa bästa praxis och överväga globala faktorer när du utvecklar custom hooks för att säkerställa att de är effektiva och tillgängliga för en mångsidig publik. Omfamna kraften i custom hooks och lyft dina React-utvecklingsfärdigheter!