Stav pre React a React Native · 1.0

Zdieľajte stav.Jeden hook.Hotovo.

Deklarujte store na jednom riadku a používajte ho v ľubovoľnom komponente, bez Provider, actions a reducerov. Komponent sa prekreslí, len keď sa zmení hodnota, ktorú vybral, a ukladá sa, keď sa rozhodnete.

Terminal
npm install @kkwaenggwari/restate

Naživo — skutočný store restate

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

Menia sa items a total: prekreslia sa tri karty, kartička zostáva.

01 — Jednoduché

Rovnaká funkcia, menej pojmov.

Štyri implementácie rovnakej úlohy vedľa seba. Čísla pochádzajú z tu zobrazeného kódu a merajú sa pri zostavení tohto webu.

Zdieľané počítadlo

Dva komponenty čítajú a menia rovnaké číslo.

Redux Toolkit Organizuje aktualizácie do slice a actions odosielaných do store poskytovaného cez kontext Reactu. Vynikajúce DevTools a prísny, predvídateľný tok aktualizácií pre veľké tímy.

Zustand Vytvára hook store pomocou create() a mení ho cez setState. Drobný a pružný, s middleware pre perzistenciu a DevTools.

Jotai Skladá atómy a odvodené atómy, voliteľne vnútri Provider. Jemné závislosti medzi mnohými malými kúskami stavu.

Redux Toolkit29 riadkov · 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 riadkov · 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 riadkov · 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 Jedna deklarácia je store aj hook zároveň.

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

Pre túto úlohu sú aj Zustand a Jotai malé a zaobídu sa bez Provider. Rozdiel rastie, keď pribudne ukladanie a stav pre každý dokument.

Výber poľa

Komponent nadpisu sa nemá prekresliť, keď sa zmení len text.

Redux Toolkit Organizuje aktualizácie do slice a actions odosielaných do store poskytovaného cez kontext Reactu. Vynikajúce DevTools a prísny, predvídateľný tok aktualizácií pre veľké tímy.

Zustand Vytvára hook store pomocou create() a mení ho cez setState. Drobný a pružný, s middleware pre perzistenciu a DevTools.

Jotai Skladá atómy a odvodené atómy, voliteľne vnútri Provider. Jemné závislosti medzi mnohými malými kúskami stavu.

Redux Toolkit26 riadkov · 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 riadkov · 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 riadkov · 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 Odovzdajte selector rovnakému hooku, ktorý už používate.

restate17 riadkov · 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>
    </>
  );
}

Vyberať vedia všetky nástroje tu. restate používa rovnaké volanie hooku namiesto samostatného hooku pre selector alebo odvodeného atómu.

Uloženie konceptu

Pri spustení obnovte uložený koncept, pri kliknutí ukladajte a zobrazte stavy ukladania, neuložené a zlyhanie.

Redux Toolkit Organizuje aktualizácie do slice a actions odosielaných do store poskytovaného cez kontext Reactu. Vynikajúce DevTools a prísny, predvídateľný tok aktualizácií pre veľké tímy.

Zustand Vytvára hook store pomocou create() a mení ho cez setState. Drobný a pružný, s middleware pre perzistenciu a DevTools.

Jotai Skladá atómy a odvodené atómy, voliteľne vnútri Provider. Jemné závislosti medzi mnohými malými kúskami stavu.

Redux Toolkit84 riadkov · 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 riadkov · 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 riadkov · 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 obnovuje automaticky a useStatus hlási ukladanie, neuložené úpravy a chyby.

restate18 riadkov · 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>
    </>
  );
}

Ostatné verzie píšu rovnaký kód IndexedDB ručne. Ich persistence middleware alebo nástroje úložiska ho môžu skrátiť adaptérom; ten kód sa tu nepočíta.

Stav pre každý dokument

Dva dokumenty majú nezávislý stav a zatvorenie jedného ho uvoľní.

Redux Toolkit Organizuje aktualizácie do slice a actions odosielaných do store poskytovaného cez kontext Reactu. Vynikajúce DevTools a prísny, predvídateľný tok aktualizácií pre veľké tímy.

Zustand Vytvára hook store pomocou create() a mení ho cez setState. Drobný a pružný, s middleware pre perzistenciu a DevTools.

Jotai Skladá atómy a odvodené atómy, voliteľne vnútri Provider. Jemné závislosti medzi mnohými malými kúskami stavu.

Redux Toolkit42 riadkov · 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 riadkov · 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 riadkov · 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 vytvára store a po odpojení ich zničí.

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

Stav môžu izolovať aj Provider alebo store v Jotai a vanilla store v Zustand; vytvárate a prepájate ich sami.

Ako sa tieto čísla počítajú

Neprázdne riadky zobrazeného kódu a názvy importované z každej knižnice stavu (importy Reactu sa nepočítajú). Každá verzia renderuje rovnaké UI s rovnakým úložiskom a rovnakými stavmi načítania a chýb. Príklady restate predpokladajú await restateManager.ready vo vstupnom bode aplikácie. Zdroje boli overené podľa oficiálnej dokumentácie každého projektu:

  • Redux Toolkit — Redux Toolkit · Quick Start, React Redux · useSelector, Redux Toolkit · configureStore. Zvoľte ho, keď je najdôležitejší jediný auditovateľný log actions a veľký ekosystém middleware
  • Zustand — Zustand · Introduction, Zustand · persist. Zvoľte ho, keď chcete čo najmenšie jadro a radšej skladáte middleware
  • Jotai — Jotai · atom, Jotai · Storage, Jotai · Provider. Zvoľte ho, keď je váš stav zo svojej podstaty grafom malých odvodených hodnôt

02 — Rýchle

Prekreslite len to, čo sa zmenilo. Zapíšte raz.

Oba panely spúšťajú skutočný balík restate vo vašom prehliadači. Klikajte, píšte a sledujte počítadlá.

Mapa renderov

Rovnaké štyri polia čítané dvoma spôsobmi. Zmeňte pole a počítajte rendery.

Číta celý store
  • Meno
  • Motív
  • Počítadlo
  • Koncept
Vyberá jedno pole
  • Meno
  • Motív
  • Počítadlo
  • Koncept

Zlúčené zápisy

autoSave počká na pauzu v písaní a potom zapíše raz. Séria volaní save() zdieľa jeden zápis vo fronte.

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

Čísla pochádzajú zo skutočných renderov Reactu a adaptéra úložiska, ktorý počíta zápisy na tejto stránke. Aj iné knižnice vedia vyberať hodnoty; tu je výber jednoducho spôsob čítania.

03 — Stabilné

Keď úložisko zlyhá, vaše údaje zostanú na mieste.

Stav sa stráca práve pri ukladaní: pomalé disky, zablokované úložisko, zatvorené karty. Toto sú záruky restate 1.0, nie rady.

Otvoriť laboratórium porúch Čítať sprievodcu spoľahlivosťou
  1. 01

    Ukladanie prebieha v poradí

    Každý store vykonáva operácie s úložiskom jednu po druhej, takže pomalé uloženie nikdy nedovolí starej hodnote prepísať novšiu.

    save() · restore()
  2. 02

    Čakajúce uloženie vezme najnovšiu hodnotu

    Volanie save(), kým iné uloženie čaká vo fronte, sa k nemu pripojí namiesto pridávania ďalších zápisov.

    save(): Promise<T>
  3. 03

    Nenačítané záznamy sa nikdy omylom neprepíšu

    Ak sa záznam nenačítal, autoSave a hromadné uloženia sa ho nedotknú a nahlásia LOAD_FAILED. Nahradiť ho môže len explicitné save().

    LOAD_FAILED
  4. 04

    Aj po odpojení sa uloženie dokončí

    Uloženia vyžiadané pred odpojením sa dokončia. Neskoršie zápisy do zničeného store sa ignorujú, rovnako ako stav Reactu po odpojení.

    useRestateScope
  5. 05

    Žiadne úložisko? Aplikácia sa aj tak spustí

    Pri renderovaní na serveri alebo so zablokovaným úložiskom store ďalej fungujú v pamäti a hlásia, že úložisko nie je dostupné.

    status().available
  6. 06

    Chyby povedia čo a kde

    Každá chyba restate nesie typovaný kód a kľúč svojho záznamu, takže môžete zobraziť správnu správu.

    error.code · error.key

Rastie s vašou aplikáciou

Zmeňte deklaráciu. Komponenty si nechajte.

Ukladanie, automatické ukladanie a validácia sú voľby toho istého store. Komponent vpravo sa nikdy nemení.

  1. Pamäť

    Zdieľaná hodnota v pamäti.

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

    Obnovuje sa automaticky; zapíše sa, keď zavoláte save().

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

    Zapíše sa raz po 500 ms pauze v písaní.

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

    Staršie záznamy sa pred použitím migrujú a kontrolujú.

    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 — v každom kroku rovnaký

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

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

Funguje všade, kde funguje React

Jeden balík pre každé prostredie Reactu.

  • react >=16.8.0

    React

    Funguje s React 16.8 a novším, v prehliadači aj pri renderovaní na serveri.

  • react-native-mmkv >=2.0.0

    React Native

    Rovnaké importy. Ukladanie použije MMKV, hneď ako nainštalujete react-native-mmkv.

  • SSR

    Renderovanie na serveri

    Store komponentu prevezmú snímku zo servera z props a začnú načítavať po hydratácii.

  • index.d.ts

    TypeScript

    Deklarácie sú súčasťou. Selektory, stavové polia a kódy chýb sú typované.

Začíname

Váš prvý zdieľaný stav za minútu.

Nainštalujte, deklarujte, používajte. Ukladanie pridajte, keď ho aplikácia bude potrebovať.

  1. 01
    Inštalácia
    Terminal
    npm install @kkwaenggwari/restate
  2. 02
    Deklarujte store
    JavaScript / JSX
    export const useCart = restate({ items: 0, total: 0 });
  3. 03
    Čítajte ho v ľubovoľnom komponente
    JavaScript / JSX
    const items = useCart((cart) => cart.items);

KKWAENGGWARI

Projekty

Hľadať v dokumentácii

Hľadajte v nadpisoch, popisoch a texte všetkých sprievodcov.