Persistance
La persistance permet à Presage de conserver les traits utilisateur et les signaux comportementaux entre les rechargements de page et les sessions navigateur.
Drivers intégrés
Section intitulée « Drivers intégrés »Driver localStorage
Section intitulée « Driver localStorage »Le driver recommandé pour les applications SaaS dans le navigateur :
import { createAdaptiveClient, createLocalStorageDriver } from '@presage-kit/core'
const client = createAdaptiveClient({ persistence: { driver: createLocalStorageDriver('my-app'), }, // ...rules})Les données sont stockées dans localStorage sous des clés namespacées :
my-app:traits— Traits utilisateur (rôle, plan, etc.)my-app:signals— Signaux comportementaux
Le préfixe évite les collisions avec d’autres bibliothèques ou plusieurs instances d’Presage sur le même domaine.
Driver en mémoire
Section intitulée « Driver en mémoire »Un driver en mémoire pour les environnements SSR et les tests :
import { createAdaptiveClient, createMemoryDriver } from '@presage-kit/core'
const client = createAdaptiveClient({ persistence: { driver: createMemoryDriver(), }, // ...rules})Les données vivent uniquement en mémoire et sont perdues quand le processus se termine. C’est utile pour :
- Le rendu côté serveur —
localStoragen’est pas disponible sur le serveur - Les tests unitaires — Pas d’effets de bord entre les exécutions de tests
- Storybook — Des stories isolées qui ne polluent pas le stockage du navigateur
Ce qui est persisté
Section intitulée « Ce qui est persisté »| Clé | Données | Quand mis à jour |
|---|---|---|
traits | Objet UserTraits | Sur identify(), updateTraits() et tout changement d’état |
signals | Objet BehavioralSignals | Après le recalcul des signaux (100ms après track()) |
Créer des drivers personnalisés
Section intitulée « Créer des drivers personnalisés »Implémentez l’interface StorageDriver pour stocker les données où vous voulez :
interface StorageDriver { get<T>(key: string): T | null set<T>(key: string, value: T): void remove(key: string): void clear(): void}Exemple : Driver sessionStorage
Section intitulée « Exemple : Driver sessionStorage »import type { StorageDriver } from '@presage-kit/core'
function createSessionStorageDriver(prefix: string): StorageDriver { function prefixed(key: string): string { return `${prefix}:${key}` }
return { get<T>(key: string): T | null { const raw = sessionStorage.getItem(prefixed(key)) if (raw === null) return null try { return JSON.parse(raw) as T } catch { return null } },
set<T>(key: string, value: T): void { sessionStorage.setItem(prefixed(key), JSON.stringify(value)) },
remove(key: string): void { sessionStorage.removeItem(prefixed(key)) },
clear(): void { const toRemove: string[] = [] for (let i = 0; i < sessionStorage.length; i++) { const key = sessionStorage.key(i) if (key?.startsWith(`${prefix}:`)) { toRemove.push(key) } } for (const key of toRemove) { sessionStorage.removeItem(key) } }, }}Exemple : Driver Cookie
Section intitulée « Exemple : Driver Cookie »import type { StorageDriver } from '@presage-kit/core'
function createCookieDriver(prefix: string): StorageDriver { return { get<T>(key: string): T | null { const name = `${prefix}_${key}` const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`)) if (!match) return null try { return JSON.parse(decodeURIComponent(match[1])) as T } catch { return null } },
set<T>(key: string, value: T): void { const name = `${prefix}_${key}` const encoded = encodeURIComponent(JSON.stringify(value)) document.cookie = `${name}=${encoded}; path=/; max-age=31536000; SameSite=Lax` },
remove(key: string): void { const name = `${prefix}_${key}` document.cookie = `${name}=; path=/; max-age=0` },
clear(): void { // Clear all cookies with the prefix const cookies = document.cookie.split('; ') for (const cookie of cookies) { const name = cookie.split('=')[0] if (name.startsWith(`${prefix}_`)) { document.cookie = `${name}=; path=/; max-age=0` } } }, }}Anti-FOUC : Initialisation synchrone
Section intitulée « Anti-FOUC : Initialisation synchrone »Un problème courant avec l’état persisté est le flash de contenu non conditionné (FOUC) : l’interface se rend avec les valeurs par défaut, puis clignote vers l’état personnalisé une fois les données chargées.
Presage évite cela car la méthode StorageDriver.get() est synchrone. Quand createAdaptiveClient() est appelé :
- Le driver lit les traits et signaux en cache de manière synchrone
- La maturité est calculée à partir des signaux en cache
- Le
UserContextinitial est prêt avant le premier rendu - Les composants reçoivent la bonne variante dès le tout premier affichage
// This is synchronous — no loading state neededconst client = createAdaptiveClient({ persistence: { driver: createLocalStorageDriver('my-app'), }, rules: [...],})
// client.getContext() already has cached dataconsole.log(client.getContext().traits) // { userId: 'user-123', role: 'admin', ... }Ce choix de conception est intentionnel. localStorage et sessionStorage sont tous deux des API synchrones, ce qui les rend idéaux pour l’anti-FOUC. Si vous construisez un driver asynchrone personnalisé (ex. IndexedDB), vous devrez gérer vous-même l’état de chargement.
Sans persistance
Section intitulée « Sans persistance »Si vous ne configurez pas de driver de persistance, Presage fonctionne quand même — mais le contexte utilisateur est réinitialisé à chaque rechargement de page. Cela peut être utile pour :
- Le prototypage et les démonstrations
- Les applications rendues côté serveur où le contexte vient du serveur
- Les scénarios où vous faites toujours un
identify()depuis votre système d’authentification au montage
// No persistence — context resets on reloadconst client = createAdaptiveClient({ rules: [...],})