Библиотеки для работы с API

Preact, как лёгкая и высокопроизводительная альтернатива React, предоставляет все базовые возможности для работы с внешними данными. Основной способ взаимодействия с API — использование встроенного fetch или сторонних библиотек, таких как Axios, в сочетании с жизненным циклом компонентов и хуками.


Использование fetch

fetch является нативным инструментом браузеров для выполнения HTTP-запросов. В Preact его удобно использовать внутри хуков useEffect, чтобы выполнять асинхронные операции при монтировании компонента.

import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';

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

    useEffect(() => {
        fetch('https://api.example.com/items')
            .then(response => {
                if (!response.ok) {
                    throw new Error(`Ошибка: ${response.status}`);
                }
                return response.json();
            })
            .then(json => setData(json))
            .catch(err => setError(err.message))
            .finally(() => setLoading(false));
    }, []);

    if (loading) return <div>Загрузка...</div>;
    if (error) return <div>{error}</div>;

    return (
        <ul>
            {data.map(item => <li key={item.id}>{item.name}</li>)}
        </ul>
    );
}

Ключевые моменты:

  • useEffect с пустым массивом зависимостей выполняется только один раз при монтировании.
  • finally позволяет корректно управлять состоянием загрузки независимо от результата запроса.
  • Обработка ошибок через catch обеспечивает устойчивость приложения.

Использование Axios

Axios предоставляет удобный API для выполнения HTTP-запросов и автоматически обрабатывает JSON. Его синтаксис более лаконичен, особенно при работе с заголовками и конфигурациями.

import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import axios from 'axios';

function AxiosFetcher() {
    const [items, setItems] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const source = axios.CancelToken.source();

        axios.get('https://api.example.com/items', { cancelToken: source.token })
            .then(response => setItems(response.data))
            .catch(err => {
                if (!axios.isCancel(err)) {
                    setError(err.message);
                }
            })
            .finally(() => setLoading(false));

        return () => {
            source.cancel('Компонент размонтирован');
        };
    }, []);

    if (loading) return <div>Загрузка данных...</div>;
    if (error) return <div>Ошибка: {error}</div>;

    return (
        <ul>
            {items.map(item => <li key={item.id}>{item.name}</li>)}
        </ul>
    );
}

Особенности использования Axios в Preact:

  • Поддержка отмены запросов при размонтировании компонента.
  • Упрощённая обработка заголовков, таймаутов и параметров запроса.
  • Чёткая интеграция с хуками для управления состоянием загрузки и ошибок.

Хуки для управления состоянием API

Для повторного использования логики запросов можно создавать кастомные хуки:

import { useState, useEffect } from 'preact/hooks';
import axios from 'axios';

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

    useEffect(() => {
        const source = axios.CancelToken.source();

        axios.get(url, { cancelToken: source.token })
            .then(response => setData(response.data))
            .catch(err => {
                if (!axios.isCancel(err)) setError(err.message);
            })
            .finally(() => setLoading(false));

        return () => source.cancel();
    }, [url]);

    return { data, loading, error };
}

Использование:

function ItemsList() {
    const { data, loading, error } = useApi('https://api.example.com/items');

    if (loading) return <div>Загрузка...</div>;
    if (error) return <div>Ошибка: {error}</div>;

    return (
        <ul>
            {data.map(item => <li key={item.id}>{item.name}</li>)}
        </ul>
    );
}

Преимущества кастомного хука:

  • Логика запроса изолирована и легко тестируется.
  • Повторное использование в нескольких компонентах без дублирования кода.
  • Гибкая обработка ошибок и состояний загрузки.

Подходы к работе с REST и GraphQL

Для REST достаточно fetch или Axios, но для GraphQL часто применяют Apollo Client или urql, которые позволяют управлять кешированием и подписками.

Пример запроса через fetch для GraphQL:

const query = `
    query {
        items {
            id
            name
        }
    }
`;

fetch('https://api.example.com/graphql', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ query })
})
.then(res => res.json())
.then(res => console.log(res.data));

Особенности работы с GraphQL:

  • Запросы выполняются POST-запросами с телом JSON.
  • Возможность запрашивать только нужные поля, минимизируя трафик.
  • Лёгкая интеграция с хуками Preact для отображения данных.

Асинхронные функции и async/await

Использование async/await делает код более читаемым и понятным:

useEffect(() => {
    let isMounted = true;

    async function fetchData() {
        try {
            const response = await fetch('https://api.example.com/items');
            if (!response.ok) throw new Error('Ошибка сети');
            const result = await response.json();
            if (isMounted) setData(result);
        } catch (err) {
            if (isMounted) setError(err.message);
        } finally {
            if (isMounted) setLoading(false);
        }
    }

    fetchData();

    return () => { isMounted = false; };
}, []);

Важные моменты:

  • Флаг isMounted предотвращает изменение состояния после размонтирования.
  • async/await упрощает цепочки промисов и делает обработку ошибок более прозрачной.
  • Код остаётся синхронным по восприятию, что облегчает поддержку.

Интеграция с глобальным состоянием

Для сложных приложений с множественными API-запросами удобно сочетать Preact с глобальными стейт-менеджерами, такими как Redux или Zustand. Это позволяет хранить результаты запросов централизованно и уменьшает количество повторных обращений к API.

import create from 'zustand';

const useStore = create(set => ({
    items: [],
    fetchItems: async () => {
        const response = await fetch('https://api.example.com/items');
        const data = await response.json();
        set({ items: data });
    }
}));

function Items() {
    const { items, fetchItems } = useStore();
    
    useEffect(() => { fetchItems(); }, []);

    return (
        <ul>
            {items.map(item => <li key={item.id}>{item.name}</li>)}
        </ul>
    );
}

Преимущества такого подхода:

  • Централизованное управление данными.
  • Упрощение повторного использования и тестирования.
  • Снижение дублирования запросов между компонентами.

Эти подходы обеспечивают гибкую и эффективную работу с API в Preact, сочетая минимализм фреймворка с современными практиками асинхронного программирования и управления состоянием.