Типизация данных для улучшения производительности
Когда мы говорим о типизации данных, первое, что приходит на ум, это интерфейсы TypeScript. С их помощью мы можем гарантировать, что данные имеют четко определенную структуру. Именно здесь TypeScript становится нашим лучшим другом.
Типизация данных от API
Рассмотрим пример: наше приложение получает список пользователей с сервера. На сервере структура ответа может быть громоздкой, с полями, которые не используются в мобильном приложении. Мы можем точно указать, что ожидаем от данных:
// Интерфейс для данных пользователя
interface User {
id: number;
name: string;
email: string;
avatarUrl: string;
}
// Пример функции для получения данных от API
async function fetchUsers(): Promise<User[]> {
const response = await fetch('https://api.example.com/users');
const data = await response.json();
// Приведение данных к интерфейсу User
return data.map((user: any) => ({
id: user.id,
name: user.name,
email: user.email,
avatarUrl: user.avatar_url, // Преобразуем ключи, если нужно
}));
}
Обратите внимание: мы используем интерфейс User, чтобы описать минимально необходимый набор данных. Если на сервере есть лишние поля, мы их преобразуем или отбрасываем. Это снижает объем данных, передаваемых в наше приложение, сохраняя только полезную информацию.
Типизация сложных структур
Иногда мы имеем дело со вложенными данными, например, посты пользователей с комментариями. В таких случаях интерфейсы помогают справляться с вложенностью:
interface Comment {
id: number;
userId: number;
content: string;
}
interface Post {
id: number;
title: string;
author: User;
comments: Comment[];
}
async function fetchPosts(): Promise<Post[]> {
const response = await fetch('https://api.example.com/posts');
const data = await response.json();
return data.map((post: any) => ({
id: post.id,
title: post.title,
author: {
id: post.author.id,
name: post.author.name,
email: post.author.email,
avatarUrl: post.author.avatar_url,
},
comments: post.comments.map((comment: any) => ({
id: comment.id,
userId: comment.user_id,
content: comment.content,
})),
}));
}
Минимизация объема передаваемых данных
Почему это важно?
Передача слишком большого объема данных может быть вызвана двумя основными проблемами:
- Передача лишних полей от API.
- Отправка слишком большого количества записей (например, передача 10 000 элементов, хотя пользователю видно только 10).
Разберем, как минимизировать эти проблемы.
Выборочно загружаем данные с сервера
Для начала, всегда уточняйте, какие данные реально нужны приложению, и передавайте это через параметры запроса. Например, сервер может поддерживать параметры фильтрации или пагинации:
async function fetchUsers(page: number, pageSize: number): Promise<User[]> {
const response = await fetch(`https://api.example.com/users?page=${page}&pageSize=${pageSize}`);
const data = await response.json();
return data.map((user: any) => ({
id: user.id,
name: user.name,
email: user.email,
avatarUrl: user.avatar_url,
}));
}
Локальная агрегация данных
Иногда есть смысл агрегировать данные на клиенте, а не загружать огромные массивы с сервера. Например, если мы хотим показать общую статистику по пользователям, разумнее загружать сразу агрегированные данные:
interface UserStatistics {
totalUsers: number;
activeUsers: number;
}
async function fetchUserStatistics(): Promise<UserStatistics> {
const response = await fetch('https://api.example.com/user-statistics');
return response.json();
}
Использование кеширования для минимизации запросов
Когда данные не меняются часто, использование кеширования может значительно снизить нагрузку на сеть. В React Native мы можем хранить данные в локальном хранилище с помощью AsyncStorage или использовать библиотеки вроде React Query для автоматического кеширования.
Пример кеширования данных в AsyncStorage:
import AsyncStorage from '@react-native-async-storage/async-storage';
async function fetchAndCacheUsers(): Promise<User[]> {
// Проверяем, есть ли данные в кеше
const cachedUsers = await AsyncStorage.getItem('users');
if (cachedUsers) {
return JSON.parse(cachedUsers);
}
// Если нет, загружаем данные с сервера
const users = await fetchUsers(1, 50); // Загружаем первую страницу
await AsyncStorage.setItem('users', JSON.stringify(users)); // Кешируем данные
return users;
}
С этим подходом, если приложение перезагружается, данные будут использоваться из локального хранилища, экономя время и сеть.
Примеры управления данными
Управление состоянием с минимизацией данных
Давайте представим, что мы используем Redux для управления состоянием. Мы можем хранить только основные данные, а остальное подгружать по необходимости.
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface UserState {
users: User[];
selectedUserId: number | null;
}
const initialState: UserState = {
users: [],
selectedUserId: null,
};
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
setUsers(state, action: PayloadAction<User[]>) {
state.users = action.payload;
},
selectUser(state, action: PayloadAction<number>) {
state.selectedUserId = action.payload;
},
},
});
export const { setUsers, selectUser } = userSlice.actions;
export default userSlice.reducer;
Теперь мы можем загружать только данные для конкретного пользователя, когда это нужно, а не загружать их заранее.
Работа с большими объемами данных
Мобильные устройства имеют ограниченные ресурсы, поэтому важно минимизировать память, используемую для обработки массивов или объектов:
- Используйте
FlatListилиVirtualizedListдля отображения длинных списков (как мы обсуждали ранее). - Убедитесь, что не храните лишние данные в состоянии или локальных переменных.
Когда вы начнете практиковать минимизацию и типизацию данных, ваши приложения будут работать быстрее, потреблять меньше памяти и выглядеть более профессионально — ведь в IT главное не только "что", но и "как".
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ