# Stockage de données SmartCommon propose plusieurs solutions pour le stockage de données côté client : * ''useGlobalStates'' : état global avec persistance automatique (localStorage/sessionStorage) * ''useStates'' : état local réactif avec path notation * ''useDb'' : base de données IndexedDB via Dexie * ''useCachedQuery'' : cache de données avec stratégies (NetworkFirst, CacheFirst, SWR) Pour la synchronisation offline complète, voir [[synchronisation|Synchronisation offline]] (''useSyncClient''). Documentation [IndexedDB](https://developer.mozilla.org/fr/docs/Web/API/IndexedDB_API) Documentation [Dexie](https://dexie.org/) Documentation [Redux Toolkit](https://redux-toolkit.js.org/) ## useGlobalStates (recommandé) Le hook ''useGlobalStates'' fournit un état global avec persistance automatique. C'est la méthode recommandée pour les données utilisateur (session, préférences, etc.). ### Utilisation basique ``` import { useGlobalStates } from '@cap-rel/smartcommon'; export const MyComponent = () => { const gst = useGlobalStates(); // Lire une valeur const user = gst.get('user'); // Écrire dans localStorage (persistant) const login = (userData) => { gst.local.set('user', userData); }; // Écrire dans sessionStorage (session uniquement) const setTempData = (data) => { gst.session.set('tempData', data); }; // Supprimer une valeur const logout = () => { gst.unset('user'); }; return (
{user ? `Bonjour ${user.name}` : 'Non connecté'}
); }; ``` ### Méthodes disponibles ^ Méthode ^ Description ^ | ''gst.get(path)'' | Récupère une valeur par son chemin | | ''gst.local.set(path, value)'' | Stocke dans localStorage (persistant) | | ''gst.session.set(path, value)'' | Stocke dans sessionStorage (session) | | ''gst.unset(path)'' | Supprime une valeur | | ''gst.values'' | Objet contenant toutes les valeurs | ### Path notation Vous pouvez accéder aux données imbriquées avec la notation par points : ``` // Définir des données imbriquées gst.local.set('user.preferences.theme', 'dark'); gst.local.set('user.preferences.language', 'fr'); // Lire des données imbriquées const theme = gst.get('user.preferences.theme'); // 'dark' const prefs = gst.get('user.preferences'); // { theme: 'dark', language: 'fr' } ``` ## useStates (état local) Le hook ''useStates'' fournit un état local réactif avec la même API que ''useGlobalStates''. ``` import { useStates } from '@cap-rel/smartcommon'; export const MyForm = () => { const st = useStates({ initialStates: { name: '', email: '', errors: {} }, debug: true // affiche les changements dans la console }); const handleChange = (field, value) => { st.set(field, value); }; const validate = () => { if (!st.get('name')) { st.set('errors.name', 'Le nom est requis'); } }; return (
handleChange('name', e.target.value)} error={st.get('errors.name')} />
); }; ``` ### Méthodes disponibles ^ Méthode ^ Description ^ | ''st.get(path)'' | Récupère une valeur | | ''st.set(path, value)'' | Définit une valeur | | ''st.unset(path)'' | Supprime une valeur | | ''st.values'' | Objet contenant toutes les valeurs | | ''st.states'' | Alias de values | ### Manipulation de tableaux ``` const st = useStates({ initialStates: { items: [] } }); // Ajouter un élément (push) st.set('items[]', { id: 1, name: 'Item 1' }); // Modifier un élément par index st.set('items[0].name', 'Item modifié'); // Supprimer un élément par index st.unset('items[0]'); ``` ## useCachedQuery (cache avec stratégies) Le hook ''useCachedQuery'' permet de mettre en cache les données API dans IndexedDB avec des stratégies configurables. ``` import { useCachedQuery } from '@cap-rel/smartcommon'; const ItemsList = () => { const db = useMemo(() => new Dexie('myApp'), []); const { data, // données (depuis cache ou réseau) isLoading, // chargement en cours isFromCache, // true si les données viennent du cache isStale, // true si les données sont périmées error, // erreur éventuelle lastFetch, // timestamp du dernier fetch refetch, // forcer un nouveau fetch invalidate, // invalider le cache } = useCachedQuery({ db, store: 'cache', // nom du store IndexedDB (schéma: 'key') key: 'items-list', // clé de cache unique fetchFn: () => api.get('items'), // fonction de récupération strategy: 'networkFirst', // stratégie de cache ttl: 3600000, // durée de vie du cache (1h par défaut) staleTime: 60000, // durée avant que le cache soit "stale" (1min par défaut) enabled: true, // activer/désactiver le fetch }); if (isLoading) return ; return ( {data?.map(item => ( {item.label} ))} ); }; ``` ### Stratégies disponibles ^ Stratégie ^ Constante ^ Comportement ^ | ''networkFirst'' | ''NETWORK_FIRST'' | Réseau d'abord, cache en fallback (défaut) | | ''cacheFirst'' | ''CACHE_FIRST'' | Cache d'abord, réseau si absent ou expiré | | ''swr'' | ''STALE_WHILE_REVALIDATE'' | Retourne le cache immédiatement, revalide en arrière-plan | ``` import { CACHE_STRATEGIES } from '@cap-rel/smartcommon'; // Utilisation avec constante useCachedQuery({ // ... strategy: CACHE_STRATEGIES.STALE_WHILE_REVALIDATE, }); ``` ## useDb (IndexedDB) Pour stocker de grandes quantités de données ou des données structurées complexes, utilisez ''useDb'' basé sur Dexie. ``` import { useDb } from '@cap-rel/smartcommon'; // Définir la base de données const db = useDb({ name: 'myApp', version: 1, stores: { items: 'id++, name, category, createdAt', categories: 'id++, name' }, debug: true }); export const ItemsManager = () => { const [items, setItems] = useState([]); // Charger les items useEffect(() => { db.items.toArray().then(setItems); }, []); // Ajouter un item const addItem = async (item) => { const id = await db.items.add(item); console.log('Item added with id:', id); }; // Modifier un item const updateItem = async (id, changes) => { await db.items.update(id, changes); }; // Supprimer un item const deleteItem = async (id) => { await db.items.delete(id); }; // Requête avec filtre const getByCategory = async (category) => { return db.items.where('category').equals(category).toArray(); }; return (/* ... */); }; ``` ### Fonctionnalités automatiques ''useDb'' ajoute automatiquement : * ''createdAt'' : timestamp de création * ''updatedAt'' : timestamp de dernière modification * Table ''logs'' : journal de toutes les opérations (create, update, delete) ### Requêtes Dexie ``` // Tous les items const all = await db.items.toArray(); // Par clé primaire const item = await db.items.get(1); // Filtrer const filtered = await db.items .where('category') .equals('electronics') .toArray(); // Trier const sorted = await db.items .orderBy('createdAt') .reverse() .toArray(); // Limiter const first10 = await db.items.limit(10).toArray(); // Compter const count = await db.items.count(); ``` Pour un cache API avec stratégies automatiques, préférez ''useCachedQuery''. Pour la synchronisation offline complète avec gestion de conflits, utilisez ''useSyncClient''. ## Redux (méthode classique) Si vous préférez utiliser Redux directement, SmartCommon fournit ''ReduxProvider''. ### Créer un slice ``` // src/redux/reducers/sessionSlice.js import { createSlice } from "@reduxjs/toolkit"; const initialState = { data: JSON.parse(sessionStorage.getItem("session")) ?? null }; const sessionSlice = createSlice({ name: "session", initialState, reducers: { setSession(state, action) { state.data = action.payload; sessionStorage.setItem("session", JSON.stringify(action.payload)); }, unsetSession(state) { state.data = null; sessionStorage.removeItem("session"); } }, }); export default sessionSlice.reducer; export const { setSession, unsetSession } = sessionSlice.actions; ``` ### Configurer le store ``` // src/redux/index.js import sessionReducer from "./reducers/sessionSlice"; import { combineReducers } from "redux"; import { configureStore } from "@reduxjs/toolkit"; const rootReducer = combineReducers({ session: sessionReducer }); export const reduxStore = configureStore({ reducer: rootReducer }); export * from "./reducers/sessionSlice"; ``` ### Utiliser dans les composants ``` import { useSelector, useDispatch } from "react-redux"; import { setSession, unsetSession } from "../../../redux"; export const MyComponent = () => { const session = useSelector(state => state.session.data); const dispatch = useDispatch(); const login = (data) => dispatch(setSession(data)); const logout = () => dispatch(unsetSession()); return (/* ... */); }; ``` ## Comparaison des méthodes ^ Méthode ^ Cas d'usage ^ Persistance ^ | ''useGlobalStates'' | Session utilisateur, préférences | localStorage / sessionStorage | | ''useStates'' | État de formulaire, UI locale | Mémoire (non persistant) | | ''useDb'' | Données structurées, stockage local | IndexedDB | | ''useCachedQuery'' | Cache API avec stratégies | IndexedDB | | ''useSyncClient'' | Synchronisation offline complète | IndexedDB | | Redux | Applications complexes, middleware | Configurable | Pour la plupart des cas, ''useGlobalStates'' et ''useStates'' suffisent. Pour un stockage IndexedDB simple, utilisez ''useDb''. Pour le cache de données API, utilisez ''useCachedQuery''. Pour le mode offline complet avec synchronisation, utilisez ''useSyncClient''. ## Voir aussi * [[hooks|Hooks]] - Documentation complète des hooks * [[requetes_api|Requêtes API]] - Combiner avec les appels API