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.
npm install @kkwaenggwari/restateV ž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.
- Preprosto0Provider, actioni in reducerji za nastavitevDeklarirajte vrednost in jo uporabite. Vnaprej ni treba ničesar povezovati.
- Hitro11 → 1Enajst sprememb, en zapisautoSave počaka na premor, zato enajst hitrih sprememb postane en zapis.
- Stabilno26Tipizirane kode napakVsaka napaka pove, kaj je šlo narobe in katerega zapisa se tiče.
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.
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>
);
}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 />
</>
);
}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.
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.
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>
);
}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>
</>
);
}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.
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.
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>
);
}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 />;
}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.
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.
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" />
</>
);
}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" />
</>
);
}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.
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.
- Ime
- Tema
- Števec
- Osnutek
- 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.
- 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() - 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> - 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 - 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 - 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 - 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.
Pomnilnik
Skupna vrednost v pomnilniku.
import { restate } from '@kkwaenggwari/restate'; export const useDraft = restate('');Shranjen
Obnovi se samodejno; zapiše se, ko pokličete save().
import { persistedRestate } from '@kkwaenggwari/restate'; export const useDraft = persistedRestate('', { key: 'draft' });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 }, });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.0React
Deluje z Reactom 16.8 in novejšim, v brskalniku in med upodabljanjem na strežniku.
react-native-mmkv >=2.0.0React Native
Isti uvozi. Shranjevanje uporabi MMKV, takoj ko namestite react-native-mmkv.
SSRUpodabljanje na strežniku
Storeji komponente vzamejo posnetek s strežnika iz props in začnejo nalaganje po hidraciji.
index.d.tsTypeScript
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.
- 01Namestitev
npm install @kkwaenggwari/restate - 02Deklarirajte store
export const useCart = restate({ items: 0, total: 0 }); - 03Berite ga v kateri koli komponenti
const items = useCart((cart) => cart.items);
KKWAENGGWARI