Zustand: Warenkorb global in React Native verwalten
Zustand: Warenkorb global verwalten
~16 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
Zustand löst BEIDE Probleme aus dem letzten Kapitel gleichzeitig: ein Store existiert AUSSERHALB des Komponentenbaums (echte Synchronisation, kein Duplikat-State mehr) UND Komponenten können sich über Selektoren GEZIELT nur auf die Werte abonnieren, die sie wirklich brauchen (kein unnötiges Re-Rendern).
Zustand installieren
npx expo install zustandstore/cartStore.js anlegen: mit Persist-Middleware
Anders als useCart, das AsyncStorage manuell in jeder Funktion aufruft, nutzen wir Zustands eingebaute persist-Middleware – sie übernimmt das Laden/Speichern automatisch im Hintergrund:
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';
export const useCartStore = create(
persist(
(set, get) => ({
cart: [],
addProduct(product) {
set({ cart: [...get().cart, product] });
},
removeProduct(sku) {
set({ cart: get().cart.filter((item) => item.sku !== sku) });
},
}),
{
name: 'cart-storage', // Schlüssel, unter dem AsyncStorage die Daten ablegt
storage: createJSONStorage(() => AsyncStorage),
}
)
);get() (neben set das zweite Argument des Store-Erzeugers) liest den AKTUELLEN Store-Zustand von INNERHALB einer Store-Funktion – get().cart statt einer eingefangenen, möglicherweise veralteten cart-Variable aus einem Closure. persist(...) umschließt die Store-Definition und übernimmt Laden beim Start UND Speichern bei JEDER set()-Änderung automatisch – kein manuelles AsyncStorage.getItem/setItem mehr nötig.
Achtung: createJSONStorage(() => AsyncStorage) ist RN-spezifisch – Zustands persist-Middleware nutzt im Web standardmäßig localStorage (synchron), in React Native braucht sie eine ASYNCHRONE Speicher-Engine wie AsyncStorage, explizit über createJSONStorage angegeben. Genau diese Anpassung fehlte in "React für Profis" Kapitel 28 (Web), weil sie dort nicht nötig war.
ProductDetailScreen.js auf useCartStore umstellen
import { useState, useEffect } from 'react';
import {
View,
Text,
Image,
ActivityIndicator,
TouchableOpacity,
StyleSheet,
} from 'react-native';
import { fetchProductBySku } from '../api/magentoApi';
import { useCartStore } from '../store/cartStore';
function ProductDetailScreen({ route }) {
const { productSku } = route.params;
const [product, setProduct] = useState(null);
const [loading, setLoading] = useState(true);
const addProduct = useCartStore((state) => state.addProduct);
useEffect(() => {
async function loadProduct() {
try {
const data = await fetchProductBySku(productSku);
setProduct(data);
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
}
loadProduct();
}, [productSku]);
if (loading) {
return <ActivityIndicator size="large" style={styles.loader} />;
}
if (!product) {
return <Text style={styles.error}>Produkt konnte nicht geladen werden.</Text>;
}
return (
<View style={styles.container}>
<Image source={{ uri: product.imageUrl }} style={styles.image} />
<Text style={styles.name}>{product.name}</Text>
<Text style={styles.price}>${product.price.toFixed(2)}</Text>
<TouchableOpacity
style={styles.button}
onPress={() => addProduct(product)}
>
<Text style={styles.buttonText}>In den Warenkorb</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 16 },
loader: { flex: 1, justifyContent: 'center' },
error: { padding: 16, color: '#dc2626' },
image: { width: '100%', height: 240, borderRadius: 12, marginBottom: 16 },
name: { fontSize: 22, fontWeight: 'bold' },
price: { fontSize: 18, color: '#2563eb', marginVertical: 8 },
button: { backgroundColor: '#2563eb', padding: 14, borderRadius: 8, alignItems: 'center', marginTop: 16 },
buttonText: { color: 'white', fontWeight: 'bold' },
});
export default ProductDetailScreen;ProductListScreen.js auf useCartStore umstellen
// In screens/ProductListScreen.js, zwei Änderungen:
// 1. Import ersetzen:
import { useCartStore } from '../store/cartStore';
// 2. Innerhalb der Komponente, statt "const { cart } = useCart();":
const cart = useCartStore((state) => state.cart);useCartStore((state) => state.cart) statt der kompletten Destrukturierung – ProductListScreen braucht NUR cart zum Anzeigen der Länge, nicht addProduct. Der Rest der Datei (Suche, FlatList, Header-Badge) bleibt UNVERÄNDERT wie in Kapitel 1 gezeigt.
Den Bug aus Kapitel 1 erneut testen
Wiederholen Sie die Schritte aus Kapitel 1: Produkt öffnen, "In den Warenkorb", zurück navigieren. Das Badge zeigt jetzt SOFORT den korrekten, aktualisierten Wert – BEIDE Screens lesen denselben, einzigen cart-Zustand aus demselben Store, keine unabhängigen Kopien mehr.
hooks/useCart.js entfernen
hooks/useCart.js wird von keiner Komponente mehr importiert – löschen Sie die Datei UND den (jetzt leeren) hooks/-Ordner. Der Store lebt ab jetzt vollständig in store/cartStore.js.
Tipp: createJSONStorage akzeptiert JEDE Speicher-Engine mit getItem/setItem/removeItem-Methoden – für sensible Daten (siehe das Passwort-Manager-Thema in der "React Native Referenz"-Serie) könnten Sie genauso expo-secure-store statt AsyncStorage einsetzen, mit identischer persist-Konfiguration.