Norsk

Lær hvordan du kan utnytte React custom hooks for å trekke ut og gjenbruke komponentlogikk, noe som forbedrer vedlikeholdbarhet, testbarhet og applikasjonsarkitektur.

React Custom Hooks: Ekstrahering av komponentlogikk for gjenbrukbarhet

React hooks har revolusjonert måten vi skriver React-komponenter på, og tilbyr en mer elegant og effektiv måte å håndtere tilstand og sideeffekter. Blant de ulike tilgjengelige hooksene, skiller custom hooks seg ut som et kraftig verktøy for å trekke ut og gjenbruke komponentlogikk. Denne artikkelen gir en omfattende guide til å forstå og implementere React custom hooks, slik at du kan bygge mer vedlikeholdbare, testbare og skalerbare applikasjoner.

Hva er React Custom Hooks?

I hovedsak er en custom hook en JavaScript-funksjon hvis navn starter med "use" og som kan kalle andre hooks. Den lar deg trekke ut komponentlogikk til gjenbrukbare funksjoner, og eliminerer dermed kodeduplisering og fremmer en renere komponentstruktur. I motsetning til vanlige React-komponenter, rendrer ikke custom hooks noe brukergrensesnitt; de kapsler bare inn logikk.

Tenk på dem som gjenbrukbare funksjoner som kan få tilgang til Reacts tilstand- og livssyklusfunksjoner. De er en fantastisk måte å dele tilstandslogikk mellom forskjellige komponenter uten å ty til 'higher-order components' eller 'render props', som ofte kan føre til kode som er vanskelig å lese og vedlikeholde.

Hvorfor bruke Custom Hooks?

Fordelene med å bruke custom hooks er mange:

Å lage din første Custom Hook

La oss illustrere opprettelsen og bruken av en custom hook med et praktisk eksempel: henting av data fra et API.

Eksempel: useFetch – En hook for datahenting

Tenk deg at du ofte trenger å hente data fra forskjellige API-er i React-applikasjonen din. I stedet for å gjenta hente-logikken i hver komponent, kan du lage 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;

Forklaring:

Bruk av useFetch-hooken i en komponent

La oss nå se hvordan vi kan bruke denne custom hooken 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;

Forklaring:

Avanserte mønstre for Custom Hooks

Utover enkel datahenting, kan custom hooks brukes til å kapsle inn mer kompleks logikk. Her er noen få avanserte mønstre:

1. Tilstandshåndtering med useReducer

For mer komplekse scenarioer for tilstandshåndtering, kan du kombinere custom hooks med useReducer. Dette lar deg håndtere tilstandsoverganger på en mer forutsigbar og organisert måte.


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;

Bruk:


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. Kontekstintegrasjon med useContext

Custom hooks kan også brukes til å forenkle tilgangen til React Context. I stedet for å bruke useContext direkte i komponentene dine, kan du lage en custom hook som kapsler inn logikken for konteksttilgang.


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

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

export default useTheme;

Bruk:


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 og Throttling

Debouncing og throttling er teknikker som brukes for å kontrollere hvor ofte en funksjon utføres. Custom hooks kan brukes til å kapsle inn denne logikken, noe som gjør det enkelt å anvende disse teknikkene på hendelseshåndterere.


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;

Bruk:


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;

Beste praksis for å skrive Custom Hooks

For å sikre at dine custom hooks er effektive og vedlikeholdbare, følg disse beste praksisene:

Globale hensyn

Når du utvikler applikasjoner for et globalt publikum, bør du huske på følgende:

Eksempel: Internasjonalisert datoformatering 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;

Bruk:


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;

Konklusjon

React custom hooks er en kraftig mekanisme for å trekke ut og gjenbruke komponentlogikk. Ved å utnytte custom hooks kan du skrive renere, mer vedlikeholdbar og testbar kode. Etter hvert som du blir mer dyktig med React, vil mestring av custom hooks betydelig forbedre din evne til å bygge komplekse og skalerbare applikasjoner. Husk å følge beste praksis og vurdere globale faktorer når du utvikler custom hooks for å sikre at de er effektive og tilgjengelige for et mangfoldig publikum. Omfavn kraften i custom hooks og løft dine React-utviklingsferdigheter!