Создание хука для работы с API
Кастомные хуки позволяют нам повторно использовать сложную бизнес-логику, такую как выполнение API-запросов, управление состоянием загрузки или сохранением данных в AsyncStorage. Они помогают держать код компонентов чистым и более читаемым, что особенно важно в крупном приложении с множеством состояний и взаимодействий.
Давайте представим, что вы разрабатываете приложение для заметок. Вам нужно, чтобы данные о заметках загружались с сервера, кэшировались в локальном хранилище, а при отсутствии интернета подгружались из AsyncStorage. Как управлять всем этим хаосом?
Начнем с реализации хука, который будет управлять процессом API-запросов. Назовем его useFetch.
Реализация useFetch
import { useState, useEffect } from "react";
import axios from "axios";
interface FetchState<T> {
data: T | null; // Загруженные данные
isLoading: boolean; // Состояние загрузки
error: string | null; // Ошибка, если произошла
}
export const useFetch = <T>(url: string) => {
const [state, setState] = useState<FetchState<T>>({
data: null,
isLoading: true,
error: null,
});
useEffect(() => {
const fetchData = async () => {
try {
setState({ data: null, isLoading: true, error: null });
const response = await axios.get<T>(url); // Отправка GET-запроса
setState({ data: response.data, isLoading: false, error: null });
} catch (error: any) {
setState({ data: null, isLoading: false, error: error.message });
}
};
fetchData();
}, [url]);
return state; // Возвращаем состояние
};
Использование useFetch
Теперь мы можем использовать этот хук для загрузки данных в наших компонентах:
import React from "react";
import { View, Text, ActivityIndicator, FlatList } from "react-native";
import { useFetch } from "./hooks/useFetch";
interface Note {
id: number;
title: string;
content: string;
}
const NotesScreen: React.FC = () => {
const { data, isLoading, error } = useFetch<Note[]>("https://api.example.com/notes");
if (isLoading) return <ActivityIndicator size="large" color="#0000ff" />;
if (error) return <Text>Error: {error}</Text>;
return (
<FlatList
data={data}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<View>
<Text>{item.title}</Text>
<Text>{item.content}</Text>
</View>
)}
/>
);
};
export default NotesScreen;
Теперь загрузка данных из API максимально проста — просто вызываем наш хук!
Создание хука для работы с AsyncStorage
Дальше мы реализуем хук для хранения данных в AsyncStorage. Этот хук будет включать операции чтения и записи данных.
Реализация useAsyncStorage
import AsyncStorage from "@react-native-async-storage/async-storage";
import { useState, useEffect } from "react";
export const useAsyncStorage = <T>(key: string, initialValue: T) => {
const [value, setValue] = useState<T>(initialValue);
// Сохранение в AsyncStorage
const saveToStorage = async (newValue: T) => {
try {
const jsonValue = JSON.stringify(newValue);
await AsyncStorage.setItem(key, jsonValue);
setValue(newValue);
} catch (error) {
console.error("Failed to save to AsyncStorage:", error);
}
};
// Загрузка из AsyncStorage
useEffect(() => {
const loadFromStorage = async () => {
try {
const jsonValue = await AsyncStorage.getItem(key);
if (jsonValue !== null) {
setValue(JSON.parse(jsonValue));
}
} catch (error) {
console.error("Failed to load from AsyncStorage:", error);
}
};
loadFromStorage();
}, [key]);
return [value, saveToStorage] as const;
};
Использование useAsyncStorage
Теперь мы можем хранить данные локально с помощью хука:
import React from "react";
import { View, Button, Text } from "react-native";
import { useAsyncStorage } from "./hooks/useAsyncStorage";
const Counter: React.FC = () => {
const [count, setCount] = useAsyncStorage<number>("counter", 0);
return (
<View>
<Text>Count: {count}</Text>
<Button title="Increment" onPress={() => setCount(count + 1)} />
</View>
);
};
export default Counter;
Каждое нажатие на кнопку обновляет значение в AsyncStorage и сразу же отображает новое значение на экране.
Комбинирование хуков для работы с API и AsyncStorage
Теперь давайте объединим оба подхода. Мы создадим хук, который будет одновременно работать с API и сохранять данные в AsyncStorage для кэширования. Назовем его useCachedFetch.
Реализация useCachedFetch
import { useFetch } from "./useFetch";
import { useAsyncStorage } from "./useAsyncStorage";
import { useEffect } from "react";
export const useCachedFetch = <T>(url: string, cacheKey: string, initialValue: T) => {
const { data, isLoading, error } = useFetch<T>(url);
const [cachedData, setCachedData] = useAsyncStorage<T>(cacheKey, initialValue);
useEffect(() => {
if (data) {
setCachedData(data); // Кэшируем данные, если они загружены
}
}, [data, setCachedData]);
return { data: cachedData || data, isLoading, error };
};
Использование useCachedFetch
import React from "react";
import { View, Text, ActivityIndicator, FlatList } from "react-native";
import { useCachedFetch } from "./hooks/useCachedFetch";
interface Note {
id: number;
title: string;
content: string;
}
const NotesScreen: React.FC = () => {
const { data, isLoading, error } = useCachedFetch<Note[]>(
"https://api.example.com/notes",
"cached_notes",
[]
);
if (isLoading) return <ActivityIndicator size="large" color="#0000ff" />;
if (error) return <Text>Error: {error}</Text>;
return (
<FlatList
data={data}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<View>
<Text>{item.title}</Text>
<Text>{item.content}</Text>
</View>
)}
/>
);
};
export default NotesScreen;
Теперь даже если у пользователя пропадет интернет, приложение будет загружать данные из кэша AsyncStorage, обеспечивая стабильную работу.
Типичные ошибки и советы
При работе с API и AsyncStorage стоит помнить о следующих нюансах:
- Никогда не храните конфиденциальные данные, такие как пароли, в
AsyncStorage, так как это небезопасно. - Обрабатывайте все ошибки при работе с сетью
try-catch, так как соединение может быть нестабильным. - Следите за актуальностью данных в кэше — обновляйте их при изменении на сервере.
Сочетание API и локального хранилища — это ключевая особенность современных мобильных приложений, которая помогает создавать быстрые и отзывчивые интерфейсы. Теперь с кастомными хуками ваш код будет не только работать, но и прекрасно выглядеть!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ