Українська

Дізнайтеся, як використовувати React custom hooks для вилучення та повторного використання логіки компонента, покращуючи підтримку коду, тестування та загальну архітектуру програми.

React Custom Hooks: Вилучення логіки компонента для повторного використання

React hooks революціонізували спосіб написання React-компонентів, пропонуючи більш елегантний та ефективний спосіб керування станом та побічними ефектами. Серед різних доступних хуків, custom hooks виділяються як потужний інструмент для вилучення та повторного використання логіки компонента. Ця стаття містить вичерпний посібник з розуміння та реалізації React custom hooks, що дозволяє створювати більш підтримувані, тестовані та масштабовані програми.

Що таке React Custom Hooks?

По суті, custom hook - це функція JavaScript, назва якої починається з "use" і може викликати інші хуки. Він дозволяє вилучати логіку компонента в функції, які можна використовувати повторно, тим самим усуваючи дублювання коду та сприяючи більш чистій структурі компонента. На відміну від звичайних React-компонентів, custom hooks не відображають жодного UI; вони просто інкапсулюють логіку.

Уявіть їх як функції, які можна використовувати повторно і які можуть отримати доступ до стану React та функцій життєвого циклу. Це чудовий спосіб поділитися логікою зі станом між різними компонентами, не вдаючись до компонентів вищого порядку або render props, що часто може призвести до коду, який важко читати та підтримувати.

Чому варто використовувати Custom Hooks?

Переваги використання custom hooks численні:

Створення вашого першого Custom Hook

Проілюструємо створення та використання custom hook на практичному прикладі: отримання даних з API.

Приклад: useFetch - Hook для отримання даних

Уявіть, що вам часто потрібно отримувати дані з різних API у вашій React-програмі. Замість повторення логіки отримання в кожному компоненті, ви можете створити хук useFetch.


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;

Пояснення:

Використання Hook useFetch у компоненті

Тепер давайте подивимося, як використовувати цей custom hook у React-компоненті:


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>Завантаження користувачів...</p>;
  if (error) return <p>Помилка: {error.message}</p>;
  if (!users) return <p>Користувачів не знайдено.</p>;

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

export default UserList;

Пояснення:

Розширені патерни Custom Hook

Окрім простого отримання даних, custom hooks можна використовувати для інкапсуляції більш складної логіки. Ось кілька розширених патернів:

1. Управління станом за допомогою useReducer

Для більш складних сценаріїв управління станом ви можете об’єднати custom hooks з useReducer. Це дозволяє керувати переходами стану більш передбачуваним і організованим способом.


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;

Використання:


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

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

  return (
    <div>
      <p>Рахунок: {count}</p>
      <button onClick={increment}>Збільшити</button>
      <button onClick={decrement}>Зменшити</button>
    </div>
  );
}

export default Counter;

2. Інтеграція контексту з useContext

Custom hooks також можна використовувати для спрощення доступу до React Context. Замість використання useContext безпосередньо у ваших компонентах, ви можете створити custom hook, який інкапсулює логіку доступу до контексту.


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

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

export default useTheme;

Використання:


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

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

  return (
    <div style={{ backgroundColor: theme.background, color: theme.color }}>
      <p>Це мій компонент.</p>
      <button onClick={toggleTheme}>Змінити тему</button>
    </div>
  );
}

export default MyComponent;

3. Debouncing і Throttling

Debouncing і throttling — це методи, які використовуються для контролю швидкості виконання функції. Custom hooks можна використовувати для інкапсуляції цієї логіки, що полегшує застосування цих методів до обробників подій.


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;

Використання:


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;

Рекомендації щодо написання Custom Hooks

Щоб ваші custom hooks були ефективними та зручними в обслуговуванні, дотримуйтеся цих рекомендацій:

Глобальні міркування

Під час розробки програм для глобальної аудиторії враховуйте наступне:

Приклад: Інтернаціоналізоване форматування дати за допомогою 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;

Використання:


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;

Висновок

React custom hooks — це потужний механізм для вилучення та повторного використання логіки компонента. Використовуючи custom hooks, ви можете писати чистіший, зручніший в обслуговуванні та тестований код. Коли ви станете більш досвідченими в React, опанування custom hooks значно покращить вашу здатність створювати складні та масштабовані програми. Не забувайте дотримуватися найкращих практик і враховуйте глобальні фактори під час розробки custom hooks, щоб переконатися, що вони ефективні та доступні для різноманітної аудиторії. Прийміть силу custom hooks і покращте свої навички розробки React!

React Custom Hooks: Вилучення логіки компонента для повторного використання | MLOG