Работа с 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();
Советы по эффективности
- Никогда не сохраняйте "тяжёлые" данные. Если нужно сохранить огромные массивы или изображения, рассмотрите использование базы данных (например, SQLite) или внешнего API.
- Помните про асинхронность. Всегда обрабатывайте
Promise, чтобы избежать зависания приложения. - Проверяйте лимиты хранилища.
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>
);
};
Текущая тема сохраняется даже после закрытия приложения — круто, правда?
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ