State for React og React Native · 1.0

Del state.Én hook.Ferdig.

Deklarer en store på én linje og bruk den i hvilken som helst komponent, uten Provider, actions eller reducers. En komponent renderes bare på nytt når verdien den valgte endres, og lagring skjer når du bestemmer.

Terminal
npm install @kkwaenggwari/restate

Live — en ekte restate-store

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

items og total endres: tre kort renderes, gavekortet står stille.

01 — Enkelt

Samme funksjon, færre begreper.

Fire implementasjoner av samme oppgave side om side. Tallene kommer fra koden som vises her, og måles når nettstedet bygges.

Delt teller

To komponenter leser og oppdaterer det samme tallet.

Redux Toolkit Organiserer oppdateringer i slices og actions som sendes til en store som leveres via React-kontekst. Utmerkede DevTools og en streng, forutsigbar oppdateringsflyt for store team.

Zustand Oppretter en store-hook med create() og endrer den med setState. Bitteliten og fleksibel, med middleware for persistens og DevTools.

Jotai Setter sammen atoms og avledede atoms, eventuelt inne i en Provider. Finkornede avhengigheter mellom mange små biter av state.

Redux Toolkit29 linjer · 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 linjer · 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 linjer · 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 Én deklarasjon er både storen og hooken.

restate13 linjer · 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 />
    </>
  );
}

For denne oppgaven er Zustand og Jotai også små og klarer seg uten Provider. Forskjellen vokser når lagring og state per dokument kommer inn.

Velg et felt

Tittelkomponenten skal ikke renderes når bare teksten endres.

Redux Toolkit Organiserer oppdateringer i slices og actions som sendes til en store som leveres via React-kontekst. Utmerkede DevTools og en streng, forutsigbar oppdateringsflyt for store team.

Zustand Oppretter en store-hook med create() og endrer den med setState. Bitteliten og fleksibel, med middleware for persistens og DevTools.

Jotai Setter sammen atoms og avledede atoms, eventuelt inne i en Provider. Finkornede avhengigheter mellom mange små biter av state.

Redux Toolkit26 linjer · 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 linjer · 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 linjer · 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 Send en selector til den samme hooken du allerede bruker.

restate17 linjer · 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 verktøyene her kan velge. restate bruker det samme hook-kallet i stedet for en egen selector-hook eller et avledet atom.

Lagre et utkast

Gjenopprett et lagret utkast ved oppstart, lagre ved klikk, og vis tilstandene lagrer, ikke lagret og mislyktes.

Redux Toolkit Organiserer oppdateringer i slices og actions som sendes til en store som leveres via React-kontekst. Utmerkede DevTools og en streng, forutsigbar oppdateringsflyt for store team.

Zustand Oppretter en store-hook med create() og endrer den med setState. Bitteliten og fleksibel, med middleware for persistens og DevTools.

Jotai Setter sammen atoms og avledede atoms, eventuelt inne i en Provider. Finkornede avhengigheter mellom mange små biter av state.

Redux Toolkit84 linjer · 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 linjer · 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 linjer · 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 gjenoppretter automatisk, og useStatus melder om lagring, ulagrede endringer og feil.

restate18 linjer · 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 andre versjonene skriver den samme IndexedDB-koden for hånd. Persistens-middleware eller lagringsverktøy der kan korte den ned med en adapter; den koden telles ikke her.

State per dokument

To dokumenter har uavhengig state, og å lukke det ene frigjør den.

Redux Toolkit Organiserer oppdateringer i slices og actions som sendes til en store som leveres via React-kontekst. Utmerkede DevTools og en streng, forutsigbar oppdateringsflyt for store team.

Zustand Oppretter en store-hook med create() og endrer den med setState. Bitteliten og fleksibel, med middleware for persistens og DevTools.

Jotai Setter sammen atoms og avledede atoms, eventuelt inne i en Provider. Finkornede avhengigheter mellom mange små biter av state.

Redux Toolkit42 linjer · 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 linjer · 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 linjer · 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 oppretter stores og ødelegger dem etter unmount.

restate27 linjer · 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" />
    </>
  );
}

En Provider eller store i Jotai og vanilla stores i Zustand kan også isolere state; du oppretter og kobler dem selv.

Slik telles tallene

Ikke-tomme linjer i koden som vises, og navn som importeres fra hvert state-bibliotek (React-importer telles ikke). Hver versjon renderer samme UI med samme lagring og samme laste- og feiltilstander. restate-eksemplene forutsetter await restateManager.ready i appens inngangspunkt. Kildene er kontrollert mot den offisielle dokumentasjonen til hvert prosjekt:

  • Redux Toolkit — Redux Toolkit · Quick Start, React Redux · useSelector, Redux Toolkit · configureStore. Velg det når én reviderbar action-logg og et stort middleware-økosystem betyr mest
  • Zustand — Zustand · Introduction, Zustand · persist. Velg det når du vil ha minst mulig kjerne og heller setter sammen middleware
  • Jotai — Jotai · atom, Jotai · Storage, Jotai · Provider. Velg det når state av natur er en graf av små avledede verdier

02 — Raskt

Render bare det som endret seg. Skriv én gang.

Begge panelene kjører den ekte restate-pakken i nettleseren din. Klikk, skriv og følg med på tellerne.

Renderingskart

De samme fire feltene lest på to måter. Endre et felt og tell renderingene.

Leser hele storen
  • Navn
  • Tema
  • Teller
  • Utkast
Velger ett felt
  • Navn
  • Tema
  • Teller
  • Utkast

Sammenslåtte skrivinger

autoSave venter på en pause i skrivingen og skriver så én gang. En rekke save()-kall deler én skriving i køen.

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

Tallene kommer fra ekte React-renderinger og en lagringsadapter som teller skrivinger på denne siden. Andre biblioteker kan også velge verdier; her er utvalg rett og slett måten å lese på.

03 — Stabilt

Når lagringen svikter, blir dataene dine liggende.

Det er ved lagring at state går tapt: trege disker, blokkert lagring, lukkede faner. Dette er garantier i restate 1.0, ikke råd.

Åpne feillaboratoriet Les guiden om pålitelighet
  1. 01

    Lagring skjer i rekkefølge

    Hver store kjører lagringsoperasjoner én om gangen, så en treg lagring lar aldri en gammel verdi overskrive en nyere.

    save() · restore()
  2. 02

    En ventende lagring tar den nyeste verdien

    Å kalle save() mens en annen lagring venter i køen, slutter seg til den i stedet for å stable flere skrivinger.

    save(): Promise<T>
  3. 03

    Uleste poster overskrives aldri ved et uhell

    Hvis en post ikke ble lastet inn, lar autoSave og masselagring den være og melder LOAD_FAILED. Bare et eksplisitt save() kan erstatte den.

    LOAD_FAILED
  4. 04

    Selv etter unmount fullføres lagringen

    Lagringer som ble bedt om før unmount, fullføres. Senere skrivinger til en ødelagt store ignoreres, akkurat som React-state etter unmount.

    useRestateScope
  5. 05

    Ingen lagring? Appen starter likevel

    Under serverrendering eller med blokkert lagring fortsetter stores i minnet og melder at lagring ikke er tilgjengelig.

    status().available
  6. 06

    Feil forteller hva og hvor

    Hver restate-feil har en typet kode og postens nøkkel, så du kan vise riktig melding.

    error.code · error.key

Vokser med appen

Endre deklarasjonen. Behold komponentene.

Lagring, automatisk lagring og validering er alternativer på den samme storen. Komponenten til høyre endres aldri.

  1. Minne

    En delt verdi i minnet.

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

    Gjenopprettes automatisk; skrives når du kaller save().

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

    Skrives én gang etter 500 ms pause i skrivingen.

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

    Eldre poster migreres og kontrolleres før bruk.

    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',
    });

Komponent — lik i hvert steg

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

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

Fungerer der React fungerer

Én pakke for alle React-miljøer.

  • react >=16.8.0

    React

    Fungerer med React 16.8 og nyere, i nettleseren og under serverrendering.

  • react-native-mmkv >=2.0.0

    React Native

    Samme importer. Lagring bruker MMKV så snart du installerer react-native-mmkv.

  • SSR

    Serverrendering

    En komponents stores tar serverens øyeblikksbilde fra props og begynner å laste inn etter hydrering.

  • index.d.ts

    TypeScript

    Deklarasjoner følger med. Selektorer, statusfelt og feilkoder er typet.

Kom i gang

Din første delte state på ett minutt.

Installer, deklarer, bruk. Legg til lagring når appen trenger det.

  1. 01
    Installer
    Terminal
    npm install @kkwaenggwari/restate
  2. 02
    Deklarer en store
    JavaScript / JSX
    export const useCart = restate({ items: 0, total: 0 });
  3. 03
    Les den i hvilken som helst komponent
    JavaScript / JSX
    const items = useCart((cart) => cart.items);

KKWAENGGWARI

Prosjekter

Søk i dokumentasjonen

Søk i titler, beskrivelser og tekst i alle guider.