State voor React en React Native · 1.0

Deel state.Eén hook.Klaar.

Declareer een store in één regel en gebruik hem in elk component, zonder Provider, actions of reducers. Een component rendert alleen opnieuw als de waarde die het selecteerde verandert, en opslaan gebeurt wanneer jij besluit.

Terminal
npm install @kkwaenggwari/restate

Live — een echte restate-store

useCart.set(cart => ({ ...cart, items: cart.items + 1, total: cart.total + 12 }))

items en total veranderen: drie kaarten renderen, de cadeaukaart blijft stil.

01 — Makkelijk

Dezelfde functionaliteit, minder begrippen.

Vier implementaties van dezelfde taak naast elkaar. De getallen komen uit de getoonde code en worden gemeten tijdens de build van deze site.

Gedeelde teller

Twee componenten lezen en wijzigen hetzelfde getal.

Redux Toolkit Organiseert updates in slices en actions die worden verstuurd naar een store die via React-context wordt geleverd. Uitstekende DevTools en een strikte, voorspelbare updatestroom voor grote teams.

Zustand Maakt een store-hook met create() en wijzigt die met setState. Piepklein en flexibel, met middleware voor opslag en DevTools.

Jotai Stelt atoms en afgeleide atoms samen, optioneel binnen een Provider. Fijnmazige afhankelijkheden tussen veel kleine stukjes state.

Redux Toolkit29 regels · 5 API
JavaScript / JSX
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { Provider, useDispatch, useSelector } from 'react-redux';

const slice = createSlice({
  name: 'count',
  initialState: 0,
  reducers: { increment: (count) => count + 1 },
});
const store = configureStore({
  reducer: { count: slice.reducer },
});

function Counter() {
  const count = useSelector((state) => state.count);
  const dispatch = useDispatch();
  return <button onClick={() => dispatch(slice.actions.increment())}>{count}</button>;
}

export default function App() {
  return (
    <Provider store={store}>
      <Counter />
      <Counter />
    </Provider>
  );
}
Zustand16 regels · 1 API
JavaScript / JSX
import { create } from 'zustand';

const useCount = create(() => ({ count: 0 }));
const increment = () =>
  useCount.setState((s) => ({
    count: s.count + 1,
  }));

function Counter() {
  const count = useCount((s) => s.count);
  return <button onClick={increment}>{count}</button>;
}

export default function App() {
  return (
    <>
      <Counter />
      <Counter />
    </>
  );
}
Jotai13 regels · 2 API
JavaScript / JSX
import { atom, useAtom } from 'jotai';

const countAtom = atom(0);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  return <button onClick={() => setCount((n) => n + 1)}>{count}</button>;
}

export default function App() {
  return (
    <>
      <Counter />
      <Counter />
    </>
  );
}

restate Eén declaratie is zowel de store als de hook.

restate13 regels · 1 API
JavaScript / JSX
import { restate } from '@kkwaenggwari/restate';

const useCount = restate(0);

function Counter() {
  const count = useCount();
  return <button onClick={() => useCount.set((n) => n + 1)}>{count}</button>;
}

export default function App() {
  return (
    <>
      <Counter />
      <Counter />
    </>
  );
}

Voor deze taak zijn Zustand en Jotai ook klein en zonder Provider. Het verschil groeit zodra opslaan en state per document erbij komen.

Selecteer een veld

Het titelcomponent moet niet renderen als alleen de tekst verandert.

Redux Toolkit Organiseert updates in slices en actions die worden verstuurd naar een store die via React-context wordt geleverd. Uitstekende DevTools en een strikte, voorspelbare updatestroom voor grote teams.

Zustand Maakt een store-hook met create() en wijzigt die met setState. Piepklein en flexibel, met middleware voor opslag en DevTools.

Jotai Stelt atoms en afgeleide atoms samen, optioneel binnen een Provider. Fijnmazige afhankelijkheden tussen veel kleine stukjes state.

Redux Toolkit26 regels · 4 API
JavaScript / JSX
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { Provider, useSelector } from 'react-redux';

const article = createSlice({
  name: 'article',
  initialState: { title: 'Hello', body: '' },
  reducers: {
    editBody: (i) => {
      i.body += '!';
    },
    editTitle: (i) => {
      i.title += '!';
    },
  },
});
const store = configureStore({ reducer: article.reducer });

function Title() {
  const title = useSelector((i) => i.title);
  return <h3>{title}</h3>;
}

export default function App() {
  return (
    <Provider store={store}>
      <Title />
      <button onClick={() => store.dispatch(article.actions.editBody())}>body + !</button>
      <button onClick={() => store.dispatch(article.actions.editTitle())}>title + !</button>
    </Provider>
  );
}
Zustand17 regels · 1 API
JavaScript / JSX
import { create } from 'zustand';

const useArticle = create(() => ({ title: 'Hello', body: '' }));

function Title() {
  const title = useArticle((i) => i.title);
  return <h3>{title}</h3>;
}

export default function App() {
  return (
    <>
      <Title />
      <button
        onClick={() =>
          useArticle.setState((i) => ({
            body: i.body + '!',
          }))
        }
      >
        body + !
      </button>
      <button
        onClick={() =>
          useArticle.setState((i) => ({
            title: i.title + '!',
          }))
        }
      >
        title + !
      </button>
    </>
  );
}
Jotai19 regels · 3 API
JavaScript / JSX
import { atom, useAtomValue, useSetAtom } from 'jotai';

const articleAtom = atom({ title: 'Hello', body: '' });
const titleAtom = atom((get) => get(articleAtom).title);

function Title() {
  const title = useAtomValue(titleAtom);
  return <h3>{title}</h3>;
}

export default function App() {
  const setArticle = useSetAtom(articleAtom);
  return (
    <>
      <Title />
      <button
        onClick={() =>
          setArticle((i) => ({
            ...i,
            body: i.body + '!',
          }))
        }
      >
        body + !
      </button>
      <button
        onClick={() =>
          setArticle((i) => ({
            ...i,
            title: i.title + '!',
          }))
        }
      >
        title + !
      </button>
    </>
  );
}

restate Geef een selector door aan dezelfde hook die je al gebruikt.

restate17 regels · 1 API
JavaScript / JSX
import { restate } from '@kkwaenggwari/restate';

const useArticle = restate({ title: 'Hello', body: '' });

function Title() {
  const title = useArticle((i) => i.title);
  return <h3>{title}</h3>;
}

export default function App() {
  return (
    <>
      <Title />
      <button
        onClick={() =>
          useArticle.set((i) => ({
            ...i,
            body: i.body + '!',
          }))
        }
      >
        body + !
      </button>
      <button
        onClick={() =>
          useArticle.set((i) => ({
            ...i,
            title: i.title + '!',
          }))
        }
      >
        title + !
      </button>
    </>
  );
}

Alle tools hier kunnen selecteren. restate gebruikt dezelfde hook-aanroep in plaats van een aparte selector-hook of een afgeleid atom.

Een concept opslaan

Zet bij het opstarten een opgeslagen concept terug, sla op bij een klik en toon de statussen opslaan, niet opgeslagen en mislukt.

Redux Toolkit Organiseert updates in slices en actions die worden verstuurd naar een store die via React-context wordt geleverd. Uitstekende DevTools en een strikte, voorspelbare updatestroom voor grote teams.

Zustand Maakt een store-hook met create() en wijzigt die met setState. Piepklein en flexibel, met middleware voor opslag en DevTools.

Jotai Stelt atoms en afgeleide atoms samen, optioneel binnen een Provider. Fijnmazige afhankelijkheden tussen veel kleine stukjes state.

Redux Toolkit84 regels · 4 API
JavaScript / JSX
import { useEffect, useState } from 'react';
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { Provider, useSelector } from 'react-redux';

function openDraftDB() {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open('draft-example', 1);
    let blocked = false;
    request.onupgradeneeded = () => {
      request.result.createObjectStore('drafts');
    };
    request.onblocked = () => {
      blocked = true;
      reject(new Error('Close other tabs to upgrade storage'));
    };
    request.onerror = () => reject(request.error);
    request.onsuccess = () => {
      const db = request.result;
      if (blocked) {
        db.close();
        return;
      }
      db.onversionchange = () => db.close();
      resolve(db);
    };
  });
}

async function storedDraft(mode, text) {
  const db = await openDraftDB();
  try {
    return await new Promise((resolve, reject) => {
      const tx = db.transaction('drafts', mode);
      const store = tx.objectStore('drafts');
      const request = mode === 'readonly' ? store.get('draft') : store.put(text, 'draft');
      tx.oncomplete = () => resolve(request.result);
      tx.onabort = () => reject(tx.error || new Error('Storage aborted'));
    });
  } finally {
    db.close();
  }
}

const draft = createSlice({
  name: 'draft',
  initialState: '',
  reducers: { edit: (_, action) => action.payload },
});
const store = configureStore({ reducer: { draft: draft.reducer } });
const hydrated = storedDraft('readonly').then((saved) => {
  if (saved === undefined) return;
  if (typeof saved !== 'string') throw new Error('Invalid draft');
  const value = saved;
  store.dispatch(draft.actions.edit(value));
});
// Observe errors immediately, even before the component mounts.
hydrated.catch(() => {});

function DraftEditor() {
  const text = useSelector((state) => state.draft);
  const [ready, setReady] = useState(false);
  const [saving, setSaving] = useState(false);
  const [message, setMessage] = useState('Loading');
  useEffect(() => {
    let active = true;
    hydrated.then(
      () => {
        if (active) {
          setReady(true);
          setMessage('Ready');
        }
      },
      () => {
        if (active) setMessage('Storage error');
      },
    );
    return () => {
      active = false;
    };
  }, []);
  async function save() {
    setSaving(true);
    try {
      await storedDraft('readwrite', store.getState().draft);
      setMessage('Saved');
    } catch {
      setMessage('Save failed');
    } finally {
      setSaving(false);
    }
  }
  return (
    <>
      <label>
        Draft
        <textarea
          value={text}
          disabled={!ready || saving}
          onChange={(event) => {
            const value = event.target.value;
            store.dispatch(draft.actions.edit(value));
            setMessage('Edited');
          }}
        />
      </label>
      <output>{text}</output>
      <button disabled={!ready || saving} onClick={save}>
        Save
      </button>
      <p>{message}</p>
    </>
  );
}

export default function App() {
  return (
    <Provider store={store}>
      <DraftEditor />
    </Provider>
  );
}
Zustand79 regels · 1 API
JavaScript / JSX
import { useEffect, useState } from 'react';
import { create } from 'zustand';

function openDraftDB() {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open('draft-example', 1);
    let blocked = false;
    request.onupgradeneeded = () => {
      request.result.createObjectStore('drafts');
    };
    request.onblocked = () => {
      blocked = true;
      reject(new Error('Close other tabs to upgrade storage'));
    };
    request.onerror = () => reject(request.error);
    request.onsuccess = () => {
      const db = request.result;
      if (blocked) {
        db.close();
        return;
      }
      db.onversionchange = () => db.close();
      resolve(db);
    };
  });
}

async function storedDraft(mode, text) {
  const db = await openDraftDB();
  try {
    return await new Promise((resolve, reject) => {
      const tx = db.transaction('drafts', mode);
      const store = tx.objectStore('drafts');
      const request = mode === 'readonly' ? store.get('draft') : store.put(text, 'draft');
      tx.oncomplete = () => resolve(request.result);
      tx.onabort = () => reject(tx.error || new Error('Storage aborted'));
    });
  } finally {
    db.close();
  }
}

const useDraft = create(() => ({ text: '' }));
const hydrated = storedDraft('readonly').then((saved) => {
  if (saved === undefined) return;
  if (typeof saved !== 'string') throw new Error('Invalid draft');
  const value = saved;
  useDraft.setState({ text: value });
});
// Observe errors immediately, even before the component mounts.
hydrated.catch(() => {});

function DraftEditor() {
  const text = useDraft((state) => state.text);
  const [ready, setReady] = useState(false);
  const [saving, setSaving] = useState(false);
  const [message, setMessage] = useState('Loading');
  useEffect(() => {
    let active = true;
    hydrated.then(
      () => {
        if (active) {
          setReady(true);
          setMessage('Ready');
        }
      },
      () => {
        if (active) setMessage('Storage error');
      },
    );
    return () => {
      active = false;
    };
  }, []);
  async function save() {
    setSaving(true);
    try {
      await storedDraft('readwrite', useDraft.getState().text);
      setMessage('Saved');
    } catch {
      setMessage('Save failed');
    } finally {
      setSaving(false);
    }
  }
  return (
    <>
      <label>
        Draft
        <textarea
          value={text}
          disabled={!ready || saving}
          onChange={(event) => {
            const value = event.target.value;
            useDraft.setState({ text: value });
            setMessage('Edited');
          }}
        />
      </label>
      <output>{text}</output>
      <button disabled={!ready || saving} onClick={save}>
        Save
      </button>
      <p>{message}</p>
    </>
  );
}

export default function App() {
  return <DraftEditor />;
}
Jotai80 regels · 3 API
JavaScript / JSX
import { useEffect, useState } from 'react';
import { atom, createStore, useAtomValue } from 'jotai';

function openDraftDB() {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open('draft-example', 1);
    let blocked = false;
    request.onupgradeneeded = () => {
      request.result.createObjectStore('drafts');
    };
    request.onblocked = () => {
      blocked = true;
      reject(new Error('Close other tabs to upgrade storage'));
    };
    request.onerror = () => reject(request.error);
    request.onsuccess = () => {
      const db = request.result;
      if (blocked) {
        db.close();
        return;
      }
      db.onversionchange = () => db.close();
      resolve(db);
    };
  });
}

async function storedDraft(mode, text) {
  const db = await openDraftDB();
  try {
    return await new Promise((resolve, reject) => {
      const tx = db.transaction('drafts', mode);
      const store = tx.objectStore('drafts');
      const request = mode === 'readonly' ? store.get('draft') : store.put(text, 'draft');
      tx.oncomplete = () => resolve(request.result);
      tx.onabort = () => reject(tx.error || new Error('Storage aborted'));
    });
  } finally {
    db.close();
  }
}

const draftAtom = atom('');
const store = createStore();
const hydrated = storedDraft('readonly').then((saved) => {
  if (saved === undefined) return;
  if (typeof saved !== 'string') throw new Error('Invalid draft');
  const value = saved;
  store.set(draftAtom, value);
});
// Observe errors immediately, even before the component mounts.
hydrated.catch(() => {});

function DraftEditor() {
  const text = useAtomValue(draftAtom, { store });
  const [ready, setReady] = useState(false);
  const [saving, setSaving] = useState(false);
  const [message, setMessage] = useState('Loading');
  useEffect(() => {
    let active = true;
    hydrated.then(
      () => {
        if (active) {
          setReady(true);
          setMessage('Ready');
        }
      },
      () => {
        if (active) setMessage('Storage error');
      },
    );
    return () => {
      active = false;
    };
  }, []);
  async function save() {
    setSaving(true);
    try {
      await storedDraft('readwrite', store.get(draftAtom));
      setMessage('Saved');
    } catch {
      setMessage('Save failed');
    } finally {
      setSaving(false);
    }
  }
  return (
    <>
      <label>
        Draft
        <textarea
          value={text}
          disabled={!ready || saving}
          onChange={(event) => {
            const value = event.target.value;
            store.set(draftAtom, value);
            setMessage('Edited');
          }}
        />
      </label>
      <output>{text}</output>
      <button disabled={!ready || saving} onClick={save}>
        Save
      </button>
      <p>{message}</p>
    </>
  );
}

export default function App() {
  return <DraftEditor />;
}

restate persistedRestate zet automatisch terug, en useStatus meldt opslaan, niet-opgeslagen bewerkingen en fouten.

restate18 regels · 1 API
JavaScript / JSX
import { persistedRestate } from '@kkwaenggwari/restate';

const useDraft = persistedRestate('', {
  key: 'draft',
  validate: (value) => typeof value === 'string',
});

export default function App() {
  const text = useDraft();
  const { dirty, operation, error } = useDraft.useStatus();
  const saving = operation === 'save';
  const message = error ? 'Save failed' : saving ? 'Saving…' : dirty ? 'Edited' : 'Saved';

  return (
    <>
      <label>
        Draft
        <textarea value={text} onChange={(event) => useDraft.set(event.target.value)} />
      </label>
      <output>{text}</output>
      <button disabled={saving} onClick={() => useDraft.save().catch(() => {})}>
        Save
      </button>
      <p>{message}</p>
    </>
  );
}

De andere versies schrijven dezelfde IndexedDB-code met de hand. Hun opslag-middleware of opslaghulpmiddelen kunnen die met een adapter inkorten; die code wordt hier niet meegeteld.

State per document

Twee documenten houden onafhankelijke state, en een ervan sluiten geeft die vrij.

Redux Toolkit Organiseert updates in slices en actions die worden verstuurd naar een store die via React-context wordt geleverd. Uitstekende DevTools en een strikte, voorspelbare updatestroom voor grote teams.

Zustand Maakt een store-hook met create() en wijzigt die met setState. Piepklein en flexibel, met middleware voor opslag en DevTools.

Jotai Stelt atoms en afgeleide atoms samen, optioneel binnen een Provider. Fijnmazige afhankelijkheden tussen veel kleine stukjes state.

Redux Toolkit42 regels · 4 API
JavaScript / JSX
import { useState } from 'react';
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { Provider, useSelector } from 'react-redux';

const draft = createSlice({
  name: 'draft',
  initialState: '',
  reducers: { edit: (_, action) => action.payload },
});
const selectionSlice = createSlice({
  name: 'selection',
  initialState: 0,
  reducers: { next: (index) => index + 1 },
});
function createDocument() {
  return {
    store: configureStore({
      reducer: {
        draft: draft.reducer,
        selection: selectionSlice.reducer,
      },
    }),
  };
}

function Document({ doc, name }) {
  const text = useSelector((state) => state.draft);
  const selection = useSelector((state) => state.selection);
  return (
    <section>
      <label>
        {name}
        <input
          value={text}
          onChange={(event) => doc.store.dispatch(draft.actions.edit(event.target.value))}
        />
      </label>
      <output>{text}</output>
      <button onClick={() => doc.store.dispatch(selectionSlice.actions.next())}>
        Selection: {selection}
      </button>
    </section>
  );
}

function DocumentSlot({ name }) {
  const [doc] = useState(createDocument);
  return (
    <Provider store={doc.store}>
      <Document doc={doc} name={name} />
    </Provider>
  );
}

export default function App() {
  const [leftOpen, setLeftOpen] = useState(true);
  return (
    <>
      <button onClick={() => setLeftOpen((open) => !open)}>
        {leftOpen ? 'Close left' : 'Open left'}
      </button>
      {leftOpen && <DocumentSlot name="Left" />}
      <DocumentSlot name="Right" />
    </>
  );
}
Zustand35 regels · 2 API
JavaScript / JSX
import { useState } from 'react';
import { useStore } from 'zustand';
import { createStore } from 'zustand/vanilla';

function createDocument() {
  return {
    draft: createStore(() => ({ text: '' })),
    selection: createStore(() => ({ index: 0 })),
  };
}

function Document({ doc, name }) {
  const text = useStore(doc.draft, (state) => state.text);
  const selection = useStore(doc.selection, (state) => state.index);
  return (
    <section>
      <label>
        {name}
        <input
          value={text}
          onChange={(event) => doc.draft.setState({ text: event.target.value })}
        />
      </label>
      <output>{text}</output>
      <button onClick={() => doc.selection.setState((s) => ({ index: s.index + 1 }))}>
        Selection: {selection}
      </button>
    </section>
  );
}

function DocumentSlot({ name }) {
  const [doc] = useState(createDocument);
  return <Document doc={doc} name={name} />;
}

export default function App() {
  const [leftOpen, setLeftOpen] = useState(true);
  return (
    <>
      <button onClick={() => setLeftOpen((open) => !open)}>
        {leftOpen ? 'Close left' : 'Open left'}
      </button>
      {leftOpen && <DocumentSlot name="Left" />}
      <DocumentSlot name="Right" />
    </>
  );
}
Jotai33 regels · 3 API
JavaScript / JSX
import { useState } from 'react';
import { atom, createStore, useAtomValue } from 'jotai';

const draftAtom = atom('');
const selectionAtom = atom(0);
function createDocument() {
  return { store: createStore() };
}

function Document({ doc, name }) {
  const text = useAtomValue(draftAtom, { store: doc.store });
  const selection = useAtomValue(selectionAtom, { store: doc.store });
  return (
    <section>
      <label>
        {name}
        <input value={text} onChange={(event) => doc.store.set(draftAtom, event.target.value)} />
      </label>
      <output>{text}</output>
      <button onClick={() => doc.store.set(selectionAtom, (n) => n + 1)}>
        Selection: {selection}
      </button>
    </section>
  );
}

function DocumentSlot({ name }) {
  const [doc] = useState(createDocument);
  return <Document doc={doc} name={name} />;
}

export default function App() {
  const [leftOpen, setLeftOpen] = useState(true);
  return (
    <>
      <button onClick={() => setLeftOpen((open) => !open)}>
        {leftOpen ? 'Close left' : 'Open left'}
      </button>
      {leftOpen && <DocumentSlot name="Left" />}
      <DocumentSlot name="Right" />
    </>
  );
}

restate useRestateScope maakt stores aan en vernietigt ze na de unmount.

restate27 regels · 2 API
JavaScript / JSX
import { useState } from 'react';
import { restate, useRestateScope } from '@kkwaenggwari/restate';

function Document({ name }) {
  const doc = useRestateScope((scope) => ({
    useDraft: restate('', { scope }),
    useSelection: restate(0, { scope }),
  }));
  const text = doc.useDraft();
  const selection = doc.useSelection();
  return (
    <section>
      <label>
        {name}
        <input value={text} onChange={(event) => doc.useDraft.set(event.target.value)} />
      </label>
      <output>{text}</output>
      <button onClick={() => doc.useSelection.set((n) => n + 1)}>Selection: {selection}</button>
    </section>
  );
}

export default function App() {
  const [leftOpen, setLeftOpen] = useState(true);
  return (
    <>
      <button onClick={() => setLeftOpen((open) => !open)}>
        {leftOpen ? 'Close left' : 'Open left'}
      </button>
      {leftOpen && <Document name="Left" />}
      <Document name="Right" />
    </>
  );
}

Een Jotai-Provider of -store en vanilla stores van Zustand kunnen state ook isoleren; aanmaken en koppelen doe je zelf.

Hoe deze getallen worden geteld

Niet-lege regels van de getoonde code en namen die uit elke state-library worden geïmporteerd (React-imports tellen niet mee). Elke versie rendert dezelfde UI met dezelfde opslag en dezelfde laad- en foutstatussen. De restate-voorbeelden gaan uit van await restateManager.ready in het toegangspunt van de app. Bronnen gecontroleerd aan de hand van de officiële documentatie van elk project:

  • Redux Toolkit — Redux Toolkit · Quick Start, React Redux · useSelector, Redux Toolkit · configureStore. Kies het als één controleerbaar actionlog en een groot middleware-ecosysteem het belangrijkst zijn
  • Zustand — Zustand · Introduction, Zustand · persist. Kies het als je de kleinste kern wilt en liever middleware samenstelt
  • Jotai — Jotai · atom, Jotai · Storage, Jotai · Provider. Kies het als je state van nature een graaf van kleine afgeleide waarden is

02 — Snel

Render alleen wat veranderde. Schrijf één keer.

Beide panelen draaien het echte restate-pakket in je browser. Klik, typ en kijk naar de tellers.

Renderkaart

Dezelfde vier velden, op twee manieren gelezen. Verander een veld en tel de renders.

Leest de hele store
  • Naam
  • Thema
  • Teller
  • Concept
Selecteert één veld
  • Naam
  • Thema
  • Teller
  • Concept

Samengevoegde schrijfacties

autoSave wacht op een pauze in het typen en schrijft dan één keer. Een reeks save()-aanroepen deelt één schrijfactie in de wachtrij.

persistedRestate('', { key: 'note', autoSave: { delayMs: 400 } });

De getallen komen uit echte React-renders en een opslagadapter die op deze pagina schrijfacties telt. Andere libraries kunnen ook waarden selecteren; hier is selecteren gewoon de manier van lezen.

03 — Stabiel

Als de opslag faalt, blijven je gegevens waar ze zijn.

Bij het opslaan raakt state kwijt: trage schijven, geblokkeerde opslag, gesloten tabbladen. Dit zijn garanties van restate 1.0, geen adviezen.

Storingslab openen Lees de gids over betrouwbaarheid
  1. 01

    Opslaan gebeurt op volgorde

    Elke store voert opslagbewerkingen na elkaar uit, dus een trage save laat nooit een oude waarde een nieuwere overschrijven.

    save() · restore()
  2. 02

    Een wachtende save neemt de nieuwste waarde

    Een save() aanroepen terwijl een andere in de wachtrij staat, sluit daarbij aan in plaats van extra schrijfacties op te stapelen.

    save(): Promise<T>
  3. 03

    Ongelezen records worden nooit per ongeluk overschreven

    Als een record niet laadde, laten autoSave en bulksaves het ongemoeid en melden ze LOAD_FAILED. Alleen een expliciete save() kan het vervangen.

    LOAD_FAILED
  4. 04

    Ook na unmount wordt de save afgemaakt

    Saves die vóór de unmount zijn aangevraagd, worden afgemaakt. Latere schrijfacties naar een vernietigde store worden genegeerd, net als React-state na unmount.

    useRestateScope
  5. 05

    Geen opslag? De app start toch

    Tijdens server-rendering of met geblokkeerde opslag blijven stores in het geheugen werken en melden ze dat opslag niet beschikbaar is.

    status().available
  6. 06

    Fouten vertellen wat en waar

    Elke restate-fout heeft een getypeerde code en de sleutel van het record, zodat je de juiste melding kunt tonen.

    error.code · error.key

Groeit mee met je app

Verander de declaratie. Houd je componenten.

Opslaan, automatisch opslaan en validatie zijn opties van dezelfde store. Het component rechts verandert nooit.

  1. Geheugen

    Een gedeelde waarde in het geheugen.

    import { restate } from '@kkwaenggwari/restate';
    
    export const useDraft = restate('');
  2. Opgeslagen

    Automatisch teruggezet; geschreven als je save() aanroept.

    import { persistedRestate } from '@kkwaenggwari/restate';
    
    export const useDraft = persistedRestate('', { key: 'draft' });
  3. Automatisch opslaan

    Eén keer geschreven na een typpauze van 500 ms.

    import { persistedRestate } from '@kkwaenggwari/restate';
    
    export const useDraft = persistedRestate('', {
      key: 'draft',
      autoSave: { delayMs: 500 },
    });
  4. Gevalideerd

    Oudere records worden gemigreerd en gecontroleerd vóór gebruik.

    import { persistedRestate } from '@kkwaenggwari/restate';
    
    export const useDraft = persistedRestate('', {
      key: 'draft',
      autoSave: { delayMs: 500 },
      version: 2,
      migrate: (value) => String(value ?? ''),
      validate: (value) => typeof value === 'string',
    });

Component — bij elke stap hetzelfde

function Editor() {
  const text = useDraft();

  return <textarea value={text} onChange={(event) => useDraft.set(event.target.value)} />;
}

Werkt waar React werkt

Eén pakket voor elke React-omgeving.

  • react >=16.8.0

    React

    Werkt met React 16.8 en nieuwer, in de browser en tijdens server-rendering.

  • react-native-mmkv >=2.0.0

    React Native

    Dezelfde imports. Opslaan gebruikt MMKV zodra je react-native-mmkv installeert.

  • SSR

    Server-rendering

    Stores van een component nemen de server-snapshot uit props en beginnen na hydration met laden.

  • index.d.ts

    TypeScript

    Declaraties inbegrepen. Selectors, statusvelden en foutcodes zijn getypeerd.

Aan de slag

Je eerste gedeelde state in een minuut.

Installeer, declareer, gebruik. Voeg opslaan toe zodra je app het nodig heeft.

  1. 01
    Installeren
    Terminal
    npm install @kkwaenggwari/restate
  2. 02
    Declareer een store
    JavaScript / JSX
    export const useCart = restate({ items: 0, total: 0 });
  3. 03
    Lees hem in elk component
    JavaScript / JSX
    const items = useCart((cart) => cart.items);

KKWAENGGWARI

Projecten

Documentatie doorzoeken

Zoek in titels, beschrijvingen en de tekst van alle gidsen.