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.
npm install @kkwaenggwari/restateLive — 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.
- Enkelt0Provider, actions og reducers som må settes oppDeklarer en verdi og bruk den. Ingenting må kobles sammen på forhånd.
- Raskt11 → 1Elleve endringer, én skrivingautoSave venter på en pause, så elleve raske endringer blir til én skriving.
- Stabilt26Typede feilkoderHver feil forteller hva som gikk galt og hvilken post det gjaldt.
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.
Oppgave Delt teller · Velg et felt · Lagre et utkast · State per dokument
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.
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 Én deklarasjon er både storen og hooken.
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.
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 Send en selector til den samme hooken du allerede bruker.
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.
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 gjenoppretter automatisk, og useStatus melder om lagring, ulagrede endringer og feil.
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.
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 oppretter stores og ødelegger dem etter unmount.
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.
- Navn
- Tema
- Teller
- Utkast
- 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.
- 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() - 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> - 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 - 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 - 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 - 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.
Minne
En delt verdi i minnet.
import { restate } from '@kkwaenggwari/restate'; export const useDraft = restate('');Lagret
Gjenopprettes automatisk; skrives når du kaller save().
import { persistedRestate } from '@kkwaenggwari/restate'; export const useDraft = persistedRestate('', { key: 'draft' });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 }, });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.0React
Fungerer med React 16.8 og nyere, i nettleseren og under serverrendering.
react-native-mmkv >=2.0.0React Native
Samme importer. Lagring bruker MMKV så snart du installerer react-native-mmkv.
SSRServerrendering
En komponents stores tar serverens øyeblikksbilde fra props og begynner å laste inn etter hydrering.
index.d.tsTypeScript
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.
- 01Installer
npm install @kkwaenggwari/restate - 02Deklarer en store
export const useCart = restate({ items: 0, total: 0 }); - 03Les den i hvilken som helst komponent
const items = useCart((cart) => cart.items);
KKWAENGGWARI