JavaRush /Курсы /Модуль 3: React /Сохранение данных в AsyncStorage и типизация объектов для...

Сохранение данных в AsyncStorage и типизация объектов для хранения

Модуль 3: React
24 уровень , 6 лекция
Открыта

Работа с JSON и сохранение данных

Сразу плохая новость для фанатов строгой типизации: AsyncStorage сам по себе не знает, что вы там храните (даже если вы сами это забыли!). Чтобы справиться с этим, мы будем активно использовать JSON вместе с интерфейсами TypeScript.

Пример: сохранение и извлечение объекта пользователя

Допустим, у нас есть объект пользователя:

interface User {
  id: number;
  name: string;
  email: string;
}

const user: User = {
  id: 1,
  name: "Иван Иванов",
  email: "ivan.ivanov@example.com",
};

Сохранение данных

Чтобы сохранить объект в AsyncStorage, его сначала нужно преобразовать в строку с помощью JSON.stringify:

import AsyncStorage from '@react-native-async-storage/async-storage';

const saveUser = async (user: User): Promise<void> => {
  try {
    await AsyncStorage.setItem("@user", JSON.stringify(user));
    console.log("Пользователь сохранён!");
  } catch (error) {
    console.error("Не удалось сохранить данные:", error);
  }
};

Обратите внимание на ключ @user. Это соглашение о наименовании ключей, которое помогает избежать конфликта с другими данными в хранилище. Вы можете использовать любые ключи, но лучше придерживаться уникальных и понятных названий.

Извлечение данных

Теперь, когда данные сохранены, их можно извлечь, распарсить и привести к типу User:

const getUser = async (): Promise<User | null> => {
  try {
    const userData = await AsyncStorage.getItem("@user");
    if (userData) {
      return JSON.parse(userData) as User;
    }
    return null;
  } catch (error) {
    console.error("Не удалось получить данные:", error);
    return null;
  }
};

Здесь важно использовать JSON.parse для преобразования строки обратно в объект. А если данные не найдены, мы возвращаем null — такая ситуация может возникнуть, например, при первом запуске приложения.

Удаление данных

Иногда бывает нужно "забыть" что-то из памяти. Например, если пользователь вышел из аккаунта:

const removeUser = async (): Promise<void> => {
  try {
    await AsyncStorage.removeItem("@user");
    console.log("Данные пользователя удалены!");
  } catch (error) {
    console.error("Не удалось удалить данные:", error);
  }
};

Типизация данных для хранения: почему это важно?

Если забыть о типизации, рано или поздно ваш код превратится в болото из строк, и вам придётся гадать, как преобразовать их обратно в объекты. TypeScript и интерфейсы как раз помогут избежать этого хаоса.

Полезный трюк: типизированный хелпер для AsyncStorage

Мы можем создать универсальный хелпер, который будет работать с любыми типами данных:

const useStorage = <T>(key: string) => {
  const save = async (value: T): Promise<void> => {
    try {
      const jsonValue = JSON.stringify(value);
      await AsyncStorage.setItem(key, jsonValue);
    } catch (error) {
      console.error(`Не удалось сохранить данные для ключа "${key}":`, error);
    }
  };

  const get = async (): Promise<T | null> => {
    try {
      const jsonValue = await AsyncStorage.getItem(key);
      return jsonValue ? (JSON.parse(jsonValue) as T) : null;
    } catch (error) {
      console.error(`Не удалось получить данные для ключа "${key}":`, error);
      return null;
    }
  };

  const remove = async (): Promise<void> => {
    try {
      await AsyncStorage.removeItem(key);
    } catch (error) {
      console.error(`Не удалось удалить данные для ключа "${key}":`, error);
    }
  };

  return { save, get, remove };
};

Теперь сохранять, загружать и удалять данные стало проще:

const userStorage = useStorage<User>("@user");

const saveUser = async () => await userStorage.save(user);
const getUser = async () => {
  const user = await userStorage.get();
  console.log("Данные пользователя:", user);
};
const removeUser = async () => await userStorage.remove();

Советы по эффективности

  1. Никогда не сохраняйте "тяжёлые" данные. Если нужно сохранить огромные массивы или изображения, рассмотрите использование базы данных (например, SQLite) или внешнего API.
  2. Помните про асинхронность. Всегда обрабатывайте Promise, чтобы избежать зависания приложения.
  3. Проверяйте лимиты хранилища. AsyncStorage не предназначен для больших объёмов данных, а иногда системы могут просто ограничить его размер (особенно на iOS).

Частые проблемы и их решения

Когда вы работаете с AsyncStorage, возможны несколько распространённых ошибок:

  • Данные не сохраняются. Убедитесь, что ваш ключ уникален и вы не забыли использовать JSON.stringify перед сохранением сложных объектов.
  • Ошибка при распаковке данных. Если структура данных изменилась между версиями приложения, старые данные могут оказаться несовместимыми. Используйте версии данных или проверки типов при JSON.parse.
  • Случайное удаление данных. Всегда удостоверьтесь, что вы знаете, что именно удаляете из хранилища, особенно если ключ передаётся через переменные.

Реалистичный пример: сохранение темы приложения

Например, вы хотите сохранять текущую тему приложения (тёмную или светлую), чтобы при перезагрузке приложения она оставалась неизменной.

Интерфейс для темы

type Theme = "light" | "dark";

Кастомный хук для работы с AsyncStorage

const useTheme = () => {
  const themeStorage = useStorage<Theme>("@theme");

  const saveTheme = async (theme: Theme) => await themeStorage.save(theme);

  const getTheme = async (): Promise<Theme> => {
    const savedTheme = await themeStorage.get();
    return savedTheme || "light"; // По умолчанию — "light"
  };

  return { saveTheme, getTheme };
};

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

const App = () => {
  const [theme, setTheme] = React.useState<Theme>("light");
  const { saveTheme, getTheme } = useTheme();

  React.useEffect(() => {
    const loadTheme = async () => {
      const savedTheme = await getTheme();
      setTheme(savedTheme);
    };

    loadTheme();
  }, []);

  const toggleTheme = async () => {
    const newTheme = theme === "light" ? "dark" : "light";
    setTheme(newTheme);
    await saveTheme(newTheme);
  };

  return (
    <View style={{ backgroundColor: theme === "light" ? "#FFF" : "#333", flex: 1 }}>
      <Text>Текущая тема: {theme}</Text>
      <Button title="Сменить тему" onPress={toggleTheme} />
            </View>
            );
};

Текущая тема сохраняется даже после закрытия приложения — круто, правда?

2
Задача
Модуль 3: React, 24 уровень, 6 лекция
Недоступна
Работа с объектами и типизация
Работа с объектами и типизация
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ