AsyncStorage — qurilmada ma'lumot saqlash
Muammodan boshlaylik
Foydalanuvchi:
- Qorong'i mavzuni tanladi — ilovani qayta ochganda yorug' bo'lib qoldi
- Onboarding ni ko'rdi — har safar qaytadan ko'rsatilmoqda
- 24-darsdagi kontaktlarni kiritdi — ilova yopilganda hammasi yo'qoldi
Vebda localStorage bor. React Native da uning o'rni — AsyncStorage: qurilmada kalit-qiymat ko'rinishida doimiy saqlash.
O'rnatish
npx expo install @react-native-async-storage/async-storage
Asosiy amallar
import AsyncStorage from '@react-native-async-storage/async-storage';
// Saqlash — faqat string
await AsyncStorage.setItem('mavzu', 'qorongi');
// O'qish — yo'q bo'lsa null
const mavzu = await AsyncStorage.getItem('mavzu'); // 'qorongi' | null
// O'chirish
await AsyncStorage.removeItem('mavzu');
// Barcha kalitlar
const kalitlar = await AsyncStorage.getAllKeys();
// Hammasini tozalash (ehtiyot bo'ling!)
await AsyncStorage.clear();
localStorage dan ikki farq:
- Asinxron —
awaitkerak (diskka yozish vaqt oladi va UI ni bloklamasligi kerak) - Faqat string — ob'ektlar uchun
JSON
Ob'ektlar
type Kontakt = { id: string; ism: string; telefon: string };
async function kontaktlarniSaqla(k: Kontakt[]) {
await AsyncStorage.setItem('kontaktlar', JSON.stringify(k));
}
async function kontaktlarniOqi(): Promise<Kontakt[]> {
const json = await AsyncStorage.getItem('kontaktlar');
if (!json) return [];
try {
return JSON.parse(json) as Kontakt[];
} catch {
return []; // buzilgan ma'lumot — ilova yiqilmasin
}
}
Tiplangan yordamchi
// lib/xotira.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
export const xotira = {
async get<T>(kalit: string, standart: T): Promise<T> {
try {
const v = await AsyncStorage.getItem(kalit);
return v === null ? standart : (JSON.parse(v) as T);
} catch {
return standart;
}
},
async set<T>(kalit: string, qiymat: T) {
await AsyncStorage.setItem(kalit, JSON.stringify(qiymat));
},
remove: (kalit: string) => AsyncStorage.removeItem(kalit),
};
const korildi = await xotira.get('onboarding_korildi', false);
await xotira.set('onboarding_korildi', true);
Onboarding — faqat bir marta
// app/_layout.tsx
const [tayyor, setTayyor] = useState(false);
const [onboardingKerak, setOnboardingKerak] = useState(false);
useEffect(() => {
xotira.get('onboarding_korildi', false).then((korildi) => {
setOnboardingKerak(!korildi);
setTayyor(true);
});
}, []);
if (!tayyor) return null; // splash ko'rinib turadi
return (
<Stack>
<Stack.Protected guard={onboardingKerak}>
<Stack.Screen name="onboarding" options={{ headerShown: false }} />
</Stack.Protected>
<Stack.Protected guard={!onboardingKerak}>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
</Stack.Protected>
</Stack>
);
Onboarding oxirida:
await xotira.set('onboarding_korildi', true);
setOnboardingKerak(false); // context yoki store orqali
Kontaktlarni saqlash — Zustand persist bilan
24-darsdagi ilovani saqlanadigan qilish. Qo'lda useEffect + AsyncStorage o'rniga — 33-darsdagi persist:
// stores/kontaktlar.ts
export const useKontaktlar = create<KontaktlarHolati>()(
persist(
(set) => ({
kontaktlar: [],
qosh: (k) => set((s) => ({ kontaktlar: [...s.kontaktlar, { ...k, id: String(Date.now()) }] })),
ochir: (id) => set((s) => ({ kontaktlar: s.kontaktlar.filter((k) => k.id !== id) })),
}),
{ name: 'kontaktlar', storage: createJSONStorage(() => AsyncStorage) }
)
);
Ilova ochilganda Zustand ma'lumotni avtomatik o'qiydi ("hydration"), har o'zgarishda — saqlaydi.
Hydration tugadimi?
Ilova ochilgan birinchi lahzada store hali bo'sh — AsyncStorage asinxron o'qiladi. "Kontakt yo'q" ekrani bir lahza ko'rinib, keyin ro'yxat paydo bo'lishi mumkin:
const tayyor = useKontaktlar.persist.hasHydrated();
// yoki
const [tayyor, setTayyor] = useState(useKontaktlar.persist.hasHydrated());
useEffect(() => useKontaktlar.persist.onFinishHydration(() => setTayyor(true)), []);
Cheklovlar — qachon AsyncStorage EMAS
| Holat | Muammo | Yechim |
|---|---|---|
| Token, parol, maxfiy kalit | Shifrlanmagan — root qilingan qurilmada o'qiladi | expo-secure-store (35-dars) |
| Katta ma'lumot (minglab yozuv) | Butun qiymat har safar o'qiladi/yoziladi; Android da hajm chegarasi bor | expo-sqlite |
| Qidiruv, filtr, saralash | Kalit-qiymat — so'rovlar yo'q | expo-sqlite |
| Fayllar, rasmlar | String sifatida — juda sekin | expo-file-system |
| Tez-tez o'qiladigan kichik sozlamalar | Asinxron — har safar await |
react-native-mmkv (sinxron, juda tez) |
AsyncStorage — kichik, maxfiy bo'lmagan ma'lumot uchun: sozlamalar, bayroqlar, kichik keshlar, qoralamalar.
SQLite — qisqacha
npx expo install expo-sqlite
import * as SQLite from 'expo-sqlite';
const db = await SQLite.openDatabaseAsync('dokon.db');
await db.execAsync(`CREATE TABLE IF NOT EXISTS eslatmalar (id INTEGER PRIMARY KEY, matn TEXT, sana TEXT)`);
await db.runAsync('INSERT INTO eslatmalar (matn, sana) VALUES (?, ?)', ['Non olish', new Date().toISOString()]);
const qatorlar = await db.getAllAsync<{ id: number; matn: string }>('SELECT * FROM eslatmalar ORDER BY id DESC');
Telefon ichidagi haqiqiy SQL baza — MySQL kursidagi bilimlar shu yerda ham ishlaydi. Placeholder ? — bu yerda ham majburiy.
Nima uchun shunday?
Nima uchun AsyncStorage asinxron, localStorage esa sinxron?
Brauzerdagi localStorage sinxron — va bu tez-tez tanqid qilinadigan dizayn xatosi: katta qiymatni o'qish butun sahifani qotiradi. Mobil ilovada UI ning har bir millisekundi muhim — 60 kadr/soniyada bitta kadr uchun 16 ms bor. Diskka murojaat esa ba'zan undan ko'p vaqt oladi. Asinxron API — o'qish/yozish fonda bo'ladi, interfeys esa silliq qoladi. (MMKV kabi kutubxonalar sinxron, lekin xotiraga xaritalangan fayl orqali juda tez ishlaydi — bu boshqa muhandislik yechimi.)
Xulosa
Eslab qoling:
AsyncStorage.setItem / getItem / removeItem— asinxron, faqat string- Ob'ektlar —
JSON.stringify/JSON.parse,try/catchbilan - Tiplangan yordamchi — standart qiymat bilan
- Onboarding, sozlamalar, qoralamalar — yaxshi nomzodlar
- Zustand
persist— avtomatik saqlash; hydration ni hisobga oling - Maxfiy — SecureStore; katta va so'rovli — SQLite; fayllar — FileSystem
Keyingi dars
35-dars: SecureStore — tokenlarni xavfsiz saqlash