Stanje za React in React Native · 1.0

Delite stanje.En hook.Končano.

Store deklarirajte v eni vrstici in ga uporabite v kateri koli komponenti, brez Providerja, actionov in reducerjev. Komponenta se znova upodobi le, ko se spremeni vrednost, ki jo je izbrala, shranjevanje pa poteka, ko se odločite.

Terminal
npm install @kkwaenggwari/restate

V živo — pravi restate store

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

Spremenita se items in total: upodobijo se tri kartice, darilna kartica miruje.

01 — Preprosto

Ista funkcionalnost, manj pojmov.

Štiri izvedbe iste naloge druga ob drugi. Številke izhajajo iz tukaj prikazane kode in se merijo ob gradnji tega spletnega mesta.

Skupni števec

Dve komponenti bereta in posodabljata isto število.

Redux Toolkit Posodobitve organizira v slice in actione, poslane storeu, ki je na voljo prek konteksta Reacta. Odlični DevTools in strog, predvidljiv tok posodobitev za velike ekipe.

Zustand Ustvari hook storea s create() in ga spreminja s setState. Drobcen in prilagodljiv, z middlewarom za trajnost in DevTools.

Jotai Sestavlja atome in izpeljane atome, po želji znotraj Providerja. Fine odvisnosti med številnimi majhnimi koščki stanja.

Redux Toolkit29 vrstic · 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 vrstic · 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 vrstic · 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 Ena deklaracija je hkrati store in hook.

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

Za to nalogo sta tudi Zustand in Jotai majhna in brez Providerja. Razlika raste, ko se dodata shranjevanje in stanje za vsak dokument.

Izbor polja

Komponenta naslova se ne bi smela upodobiti, ko se spremeni le besedilo.

Redux Toolkit Posodobitve organizira v slice in actione, poslane storeu, ki je na voljo prek konteksta Reacta. Odlični DevTools in strog, predvidljiv tok posodobitev za velike ekipe.

Zustand Ustvari hook storea s create() in ga spreminja s setState. Drobcen in prilagodljiv, z middlewarom za trajnost in DevTools.

Jotai Sestavlja atome in izpeljane atome, po želji znotraj Providerja. Fine odvisnosti med številnimi majhnimi koščki stanja.

Redux Toolkit26 vrstic · 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 vrstic · 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 vrstic · 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 Podajte selector istemu hooku, ki ga že uporabljate.

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

Vsa orodja tukaj znajo izbirati. restate uporablja isti klic hooka namesto ločenega hooka za selector ali izpeljanega atoma.

Shranjevanje osnutka

Ob zagonu obnovite shranjen osnutek, shranite ob kliku in prikažite stanja shranjevanja, neshranjenega in neuspeha.

Redux Toolkit Posodobitve organizira v slice in actione, poslane storeu, ki je na voljo prek konteksta Reacta. Odlični DevTools in strog, predvidljiv tok posodobitev za velike ekipe.

Zustand Ustvari hook storea s create() in ga spreminja s setState. Drobcen in prilagodljiv, z middlewarom za trajnost in DevTools.

Jotai Sestavlja atome in izpeljane atome, po želji znotraj Providerja. Fine odvisnosti med številnimi majhnimi koščki stanja.

Redux Toolkit84 vrstic · 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 vrstic · 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 vrstic · 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 obnavlja samodejno, useStatus pa javlja shranjevanje, neshranjene spremembe in napake.

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

Druge različice isto kodo za IndexedDB pišejo ročno. Njihov middleware za trajnost ali orodja za shrambo jo lahko skrajšajo z vmesnikom; ta koda se tukaj ne šteje.

Stanje za vsak dokument

Dva dokumenta hranita neodvisno stanje, zaprtje enega pa ga sprosti.

Redux Toolkit Posodobitve organizira v slice in actione, poslane storeu, ki je na voljo prek konteksta Reacta. Odlični DevTools in strog, predvidljiv tok posodobitev za velike ekipe.

Zustand Ustvari hook storea s create() in ga spreminja s setState. Drobcen in prilagodljiv, z middlewarom za trajnost in DevTools.

Jotai Sestavlja atome in izpeljane atome, po želji znotraj Providerja. Fine odvisnosti med številnimi majhnimi koščki stanja.

Redux Toolkit42 vrstic · 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 vrstic · 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 vrstic · 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 ustvari storeje in jih po odklopu uniči.

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

Stanje lahko izolirajo tudi Provider ali store v Jotaiju in vanilla storeji v Zustandu; ustvarite in povežete jih sami.

Kako se štejejo te številke

Neprazne vrstice prikazane kode in imena, uvožena iz vsake knjižnice stanja (uvozi Reacta se ne štejejo). Vsaka različica upodobi enak UI z enako shrambo ter enakimi stanji nalaganja in napak. Primeri restate predpostavljajo await restateManager.ready v vstopni točki aplikacije. Viri so preverjeni z uradno dokumentacijo vsakega projekta:

  • Redux Toolkit — Redux Toolkit · Quick Start, React Redux · useSelector, Redux Toolkit · configureStore. Izberite ga, ko sta najpomembnejša en preverljiv dnevnik actionov in velik ekosistem middlewara
  • Zustand — Zustand · Introduction, Zustand · persist. Izberite ga, ko želite najmanjše možno jedro in raje sestavljate middleware
  • Jotai — Jotai · atom, Jotai · Storage, Jotai · Provider. Izberite ga, ko je vaše stanje po naravi graf majhnih izpeljanih vrednosti

02 — Hitro

Upodobite le spremenjeno. Zapišite enkrat.

Obe plošči poganjata pravi paket restate v vašem brskalniku. Klikajte, tipkajte in spremljajte števce.

Zemljevid upodobitev

Ista štiri polja, prebrana na dva načina. Spremenite polje in štejte upodobitve.

Bere celoten store
  • Ime
  • Tema
  • Števec
  • Osnutek
Izbere eno polje
  • Ime
  • Tema
  • Števec
  • Osnutek

Združeni zapisi

autoSave počaka na premor v tipkanju in nato zapiše enkrat. Niz klicev save() si deli en zapis v čakalni vrsti.

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

Številke izhajajo iz pravih upodobitev Reacta in vmesnika za shrambo, ki šteje zapise na tej strani. Tudi druge knjižnice znajo izbirati vrednosti; tukaj je izbor preprosto način branja.

03 — Stabilno

Ko shramba odpove, vaši podatki ostanejo na mestu.

Stanje se izgubi prav pri shranjevanju: počasni diski, blokirana shramba, zaprti zavihki. To so jamstva restate 1.0, ne nasveti.

Odpri laboratorij okvar Preberite vodnik o zanesljivosti
  1. 01

    Shranjevanja potekajo po vrsti

    Vsak store izvaja operacije s shrambo eno za drugo, zato počasno shranjevanje nikoli ne dovoli, da bi stara vrednost prepisala novejšo.

    save() · restore()
  2. 02

    Čakajoče shranjevanje vzame najnovejšo vrednost

    Klic save(), medtem ko drugo shranjevanje čaka v vrsti, se mu pridruži, namesto da bi kopičil dodatne zapise.

    save(): Promise<T>
  3. 03

    Neprebrani zapisi se nikoli pomotoma ne prepišejo

    Če se zapis ni naložil, ga autoSave in skupna shranjevanja ne dotaknejo in javijo LOAD_FAILED. Zamenja ga lahko le izrecni save().

    LOAD_FAILED
  4. 04

    Tudi po odklopu se shranjevanje zaključi

    Shranjevanja, zahtevana pred odklopom, se zaključijo. Poznejši zapisi v uničen store se prezrejo, tako kot stanje Reacta po odklopu.

    useRestateScope
  5. 05

    Ni shrambe? Aplikacija se vseeno zažene

    Med upodabljanjem na strežniku ali pri blokirani shrambi storeji še naprej delujejo v pomnilniku in javljajo, da shramba ni na voljo.

    status().available
  6. 06

    Napake povedo, kaj in kje

    Vsaka napaka restate nosi tipizirano kodo in ključ svojega zapisa, da lahko prikažete pravo sporočilo.

    error.code · error.key

Raste z vašo aplikacijo

Spremenite deklaracijo. Komponente obdržite.

Shranjevanje, samodejno shranjevanje in preverjanje so možnosti istega storea. Komponenta na desni se nikoli ne spremeni.

  1. Pomnilnik

    Skupna vrednost v pomnilniku.

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

    Obnovi se samodejno; zapiše se, ko pokličete save().

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

    Zapiše se enkrat po 500 ms premora v tipkanju.

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

    Starejši zapisi se pred uporabo migrirajo in preverijo.

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

Komponenta — ista v vsakem koraku

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

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

Deluje povsod, kjer deluje React

En paket za vsako okolje Reacta.

  • react >=16.8.0

    React

    Deluje z Reactom 16.8 in novejšim, v brskalniku in med upodabljanjem na strežniku.

  • react-native-mmkv >=2.0.0

    React Native

    Isti uvozi. Shranjevanje uporabi MMKV, takoj ko namestite react-native-mmkv.

  • SSR

    Upodabljanje na strežniku

    Storeji komponente vzamejo posnetek s strežnika iz props in začnejo nalaganje po hidraciji.

  • index.d.ts

    TypeScript

    Deklaracije so priložene. Selektorji, polja stanja in kode napak so tipizirani.

Začetek

Vaše prvo skupno stanje v minuti.

Namestite, deklarirajte, uporabite. Shranjevanje dodajte, ko ga aplikacija potrebuje.

  1. 01
    Namestitev
    Terminal
    npm install @kkwaenggwari/restate
  2. 02
    Deklarirajte store
    JavaScript / JSX
    export const useCart = restate({ items: 0, total: 0 });
  3. 03
    Berite ga v kateri koli komponenti
    JavaScript / JSX
    const items = useCart((cart) => cart.items);

KKWAENGGWARI

Projekti

Iskanje po dokumentaciji

Iščite po naslovih, opisih in besedilu vseh vodnikov.