restate / v1.0.0

Fiabilité dès la conception

Ce que restate garantit quand le stockage est lent, échoue ou est absent, et comment lire les erreurs qu’il signale.

Sur cette pageLes garantiesEnregistrements ordonnésEssayerQuand le chargement échoueEnregistrer et fermerAucun stockage disponibleValider et migrer les données enregistréesUtiliser votre propre stockageLimites et délaisCodes d’erreur

Les garanties

Ces comportements font partie du contrat de la 1.0 et sont couverts par les tests du paquet.

SituationCe que fait restate
Plusieurs enregistrements à la suiteLes exécute dans l’ordre pour chaque store ; un enregistrement en file d’attente écrit la valeur la plus récente.
Un enregistrement n’a pas pu être chargéautoSave et les enregistrements groupés l’ignorent avec LOAD_FAILED ; seul un save() explicite le remplace.
Le composant est démonté pendant un enregistrementL’enregistrement demandé se termine ; les écritures ultérieures dans le store détruit sont ignorées.
Le stockage est indisponibleLe store fonctionne en mémoire, status().available vaut false, les enregistrements sont rejetés.
Un adaptateur se bloqueL’opération expire après operationTimeoutMs et le travail suivant attend que l’adaptateur se termine.
Trop de travail en attenteLes nouvelles opérations sont rejetées avec QUEUE_FULL au lieu de s’accumuler sans limite.

Enregistrements ordonnés

Les opérations d’un même store ne se chevauchent jamais. Un enregistrement émis pendant qu’un autre attend prend la valeur la plus récente : enregistrer à chaque changement écrit le dernier état sans remplir la file.

JavaScript / JSX
useDraft.set('A');
const first = useDraft.save(); // writes "A"
useDraft.set('B');
const second = useDraft.save(); // waits in the queue
useDraft.set('C');
const third = useDraft.save(); // joins the waiting save

await Promise.all([first, second, third]); // ['A', 'C', 'C'] — two writes

Essayer

Ralentissez le stockage, faites échouer les écritures ou supprimez-le complètement.

Préparation de l’exemple…

Quand le chargement échoue

restateManager.ready est rejeté avec BULK_OPERATION_FAILED et liste chaque store en échec. Tant que cet enregistrement n’est pas chargé, autoSave et saveAll ne le remplacent pas et signalent LOAD_FAILED.

Relancez initialize() pour conserver vos modifications et les enregistrer, appelez restore() pour les abandonner, ou save() pour écraser l’enregistrement volontairement.

JavaScript / JSX
try {
  await restateManager.ready;
} catch (error) {
  // BULK_OPERATION_FAILED: one entry per store that could not load.
  for (const { store, error: cause } of error.failures) {
    console.error(store.key, cause.code);
  }
}

Enregistrer et fermer

Un enregistrement demandé avant le démontage se termine quand même : un gestionnaire « Enregistrer et fermer » n’a pas besoin d’attendre.

JavaScript / JSX
function Document({ onClose }) {
  const useDraft = useRestateScope((scope) => persistedRestate('', { scope, key: 'draft' }));

  const saveAndClose = () => {
    useDraft.save(); // finishes even after this component unmounts
    onClose();
  };
  // …
}

Aucun stockage disponible

Le rendu serveur et un stockage bloqué par le navigateur ne sont pas des erreurs au démarrage. Vérifiez available pour indiquer aux utilisateurs que les modifications ne sont conservées qu’en mémoire.

JavaScript / JSX
const { available } = useDraft.status();

if (!available) {
  // Server rendering or blocked browser storage: the store works in memory
  // and save() rejects with STORAGE_UNAVAILABLE.
  showNotice('Changes are kept until you close this tab.');
}

Valider et migrer les données enregistrées

version, migrate et validate vous protègent des enregistrements anciens ou corrompus. validate s’exécute avant l’enregistrement et après la restauration ou la migration.

JavaScript / JSX
import { persistedRestate } from '@kkwaenggwari/restate';

const useSettings = persistedRestate(
  { theme: 'system' },
  {
    key: 'settings',
    version: 2,
    migrate(value, from, to) {
      if (from === 1 && to === 2 && typeof value?.darkMode === 'boolean') {
        return { theme: value.darkMode ? 'dark' : 'light' };
      }
      throw new Error('Unsupported stored version');
    },
    validate: (value) => ['system', 'light', 'dark'].includes(value?.theme),
  },
);

Utiliser votre propre stockage

N’importe quel objet doté de getItem, setItem et removeItem convient. Les méthodes peuvent renvoyer des valeurs ou des Promises ; isAvailable est facultatif.

JavaScript / JSX
import { persistedRestate } from '@kkwaenggwari/restate';

const sessionStorageAdapter = {
  isAvailable: () => typeof sessionStorage !== 'undefined',
  getItem: (key) => sessionStorage.getItem(key),
  setItem: (key, value) => sessionStorage.setItem(key, value),
  removeItem: (key) => sessionStorage.removeItem(key),
};

export const useFilters = persistedRestate(
  { query: '' },
  {
    key: 'filters',
    storage: sessionStorageAdapter,
  },
);

Limites et délais

Réglez la durée maximale d’une opération, le nombre d’opérations en attente et la taille maximale d’une valeur.

JavaScript / JSX
persistedRestate(value, {
  key: 'draft',
  operationTimeoutMs: 10_000, // default 30000
  maxPendingOperations: 64, // default 256
  maxStorageCharacters: 1_000_000,
});

Codes d’erreur

Les erreurs créées par restate ont le name RestateError, un code et, pour les stores enregistrés, key. Les adaptateurs de stockage et vos callbacks peuvent rejeter avec leurs propres erreurs.

CodeSignification
BULK_OPERATION_FAILEDUn appel groupé comme restateManager.ready ou saveAll a rencontré des échecs ; voir error.failures.
INDEXEDDB_ABORTEDUne transaction IndexedDB a été annulée.
INDEXEDDB_BLOCKEDL’ouverture de la base de données a été bloquée par une connexion dans un autre onglet.
INDEXEDDB_CONNECTION_CLOSINGLa connexion à la base de données se fermait au début de la requête.
INDEXEDDB_OPEN_FAILEDImpossible d’ouvrir la base de données.
INDEXEDDB_REQUEST_FAILEDUne requête IndexedDB a échoué.
INDEXEDDB_STORE_MISSINGLe magasin d’objets n’existe pas dans une base déjà créée.
INVALID_STORAGE_RECORDUn enregistrement sauvegardé n’est pas un enregistrement restate valide.
INVALID_STORED_VALUEUne valeur n’a pas passé validate.
LIFECYCLE_LIMITUn updater ne cessait de démarrer et de s’arrêter.
LOAD_FAILEDUn enregistrement n’a pas pu être chargé, donc un enregistrement automatique ou groupé ne l’a pas remplacé.
MMKV_UNAVAILABLEImpossible de charger react-native-mmkv.
MULTIPLE_ERRORSPlusieurs callbacks ont échoué au cours d’une opération ; voir error.errors.
NOTIFICATION_LIMITDes observateurs ont continué de programmer des changements en boucle.
OBSERVER_ERRORUn listener ou un callback de cycle de vie a levé une erreur.
OPERATION_ABORTEDUne opération de stockage a été annulée.
QUEUE_FULLPlus d’opérations attendaient que ne l’autorise maxPendingOperations.
REENTRANT_SETTERUn updater d’état a tenté de modifier le même store pendant son exécution.
SCOPE_DESTROYEDUn scope détruit a été utilisé pour créer des stores.
SEQUENCE_EXHAUSTEDUne séquence interne n’a plus d’identifiants disponibles.
STORAGE_BLOCKEDUn appel précédent à l’adaptateur ne s’est pas terminé après un délai dépassé ou une annulation.
STORAGE_TIMEOUTUne opération a duré plus longtemps que operationTimeoutMs.
STORAGE_UNAVAILABLELe stockage n’est pas utilisable ici ; le store continue de fonctionner en mémoire.
STORAGE_VALUE_TOO_LARGELa valeur encodée dépasse maxStorageCharacters.
STORAGE_VERSION_MISMATCHUn enregistrement a une autre version et aucune fonction migrate ne le prend en charge.
STORE_DESTROYEDOn s’est abonné à un store détruit ou on lui a demandé d’utiliser le stockage.

Rechercher dans la documentation

Recherche dans les titres, les descriptions et le texte de tous les guides.