Būsena React ir React Native · 1.0
Bendrinkite būseną.Vienas hook.Viskas.
Aprašykite store viena eilute ir naudokite jį bet kuriame komponente be Provider, action ir reducer. Komponentas perpiešiamas tik pasikeitus jo pasirinktai reikšmei, o išsaugojimas vyksta tada, kai nusprendžiate.
npm install @kkwaenggwari/restateGyvai – tikras restate store
useCart.set(cart => ({ ...cart, items: cart.items + 1, total: cart.total + 12 }))Pasikeičia items ir total: perpiešiamos trys kortelės, dovanų kortelė lieka vietoje.
- Paprasta0Provider, action ir reducer nustatymuiAprašykite reikšmę ir naudokite ją. Nieko nereikia prijungti iš anksto.
- Greita11 → 1Vienuolika pakeitimų, vienas įrašymasautoSave laukia pauzės, todėl vienuolika greitų pakeitimų virsta vienu įrašymu.
- Stabilu26Tipizuoti klaidų kodaiKiekviena klaida pasako, kas nutiko ir kurio įrašo ji liečia.
01 – Paprasta
Ta pati funkcija, mažiau sąvokų.
Keturi tos pačios užduoties įgyvendinimai greta. Skaičiai gauti iš čia rodomo kodo ir matuojami kuriant šią svetainę.
Užduotis Bendras skaitiklis · Lauko pasirinkimas · Juodraščio išsaugojimas · Būsena kiekvienam dokumentui
Bendras skaitiklis
Du komponentai skaito ir atnaujina tą patį skaičių.
Redux Toolkit Atnaujinimus organizuoja į slice ir action, siunčiamus į store, pateiktą per React kontekstą. Puikūs DevTools ir griežtas, nuspėjamas atnaujinimų srautas didelėms komandoms.
Zustand Su create() sukuria store hook ir keičia jį per setState. Mažytis ir lankstus, su middleware išliekamumui ir DevTools.
Jotai Sudeda atom ir išvestinius atom, pasirinktinai Provider viduje. Smulkios priklausomybės tarp daugybės mažų būsenos dalių.
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 Vienas aprašas yra ir store, ir 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 />
</>
);
}Šiai užduočiai Zustand ir Jotai taip pat yra maži ir veikia be Provider. Skirtumas auga pridėjus išsaugojimą ir būseną kiekvienam dokumentui.
Lauko pasirinkimas
Antraštės komponentas neturėtų būti perpieštas, kai pasikeičia tik tekstas.
Redux Toolkit Atnaujinimus organizuoja į slice ir action, siunčiamus į store, pateiktą per React kontekstą. Puikūs DevTools ir griežtas, nuspėjamas atnaujinimų srautas didelėms komandoms.
Zustand Su create() sukuria store hook ir keičia jį per setState. Mažytis ir lankstus, su middleware išliekamumui ir DevTools.
Jotai Sudeda atom ir išvestinius atom, pasirinktinai Provider viduje. Smulkios priklausomybės tarp daugybės mažų būsenos dalių.
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 Perduokite selector tam pačiam hook, kurį jau naudojate.
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>
</>
);
}Visi čia esantys įrankiai gali pasirinkti. restate naudoja tą patį hook kvietimą vietoje atskiro selector hook ar išvestinio atom.
Juodraščio išsaugojimas
Paleidžiant atkurkite išsaugotą juodraštį, išsaugokite paspaudus ir rodykite saugojimo, neišsaugoto ir nepavykusio išsaugojimo būsenas.
Redux Toolkit Atnaujinimus organizuoja į slice ir action, siunčiamus į store, pateiktą per React kontekstą. Puikūs DevTools ir griežtas, nuspėjamas atnaujinimų srautas didelėms komandoms.
Zustand Su create() sukuria store hook ir keičia jį per setState. Mažytis ir lankstus, su middleware išliekamumui ir DevTools.
Jotai Sudeda atom ir išvestinius atom, pasirinktinai Provider viduje. Smulkios priklausomybės tarp daugybės mažų būsenos dalių.
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 atkuria automatiškai, o useStatus praneša apie išsaugojimą, neišsaugotus pakeitimus ir klaidas.
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>
</>
);
}Kitos versijos tą patį IndexedDB kodą rašo ranka. Jų išliekamumo middleware ar saugyklos įrankiai gali jį sutrumpinti su adapteriu; tas kodas čia neskaičiuojamas.
Būsena kiekvienam dokumentui
Du dokumentai laiko nepriklausomą būseną, o vieno uždarymas ją atlaisvina.
Redux Toolkit Atnaujinimus organizuoja į slice ir action, siunčiamus į store, pateiktą per React kontekstą. Puikūs DevTools ir griežtas, nuspėjamas atnaujinimų srautas didelėms komandoms.
Zustand Su create() sukuria store hook ir keičia jį per setState. Mažytis ir lankstus, su middleware išliekamumui ir DevTools.
Jotai Sudeda atom ir išvestinius atom, pasirinktinai Provider viduje. Smulkios priklausomybės tarp daugybės mažų būsenos dalių.
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 sukuria store ir juos sunaikina po atjungimo.
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" />
</>
);
}Būseną taip pat gali izoliuoti Provider ar store Jotai bibliotekoje ir vanilla store Zustand bibliotekoje; juos sukuriate ir prijungiate patys.
Kaip skaičiuojami šie skaičiai
Netuščios rodomo kodo eilutės ir pavadinimai, importuoti iš kiekvienos būsenos bibliotekos (React importai neskaičiuojami). Kiekviena versija atvaizduoja tą pačią UI su ta pačia saugykla ir tomis pačiomis įkėlimo bei klaidų būsenomis. restate pavyzdžiai daro prielaidą, kad programos įėjimo taške yra await restateManager.ready. Šaltiniai patikrinti pagal kiekvieno projekto oficialią dokumentaciją:
- Redux Toolkit — Redux Toolkit · Quick Start, React Redux · useSelector, Redux Toolkit · configureStore. Rinkitės, kai svarbiausi yra audituojamas action žurnalas ir didelė middleware ekosistema
- Zustand — Zustand · Introduction, Zustand · persist. Rinkitės, kai norite mažiausio branduolio ir mieliau patys sudedate middleware
- Jotai — Jotai · atom, Jotai · Storage, Jotai · Provider. Rinkitės, kai jūsų būsena iš prigimties yra mažų išvestinių reikšmių grafas
02 – Greita
Perpieškite tik tai, kas pasikeitė. Rašykite kartą.
Abu skydeliai jūsų naršyklėje vykdo tikrą restate paketą. Spauskite, rašykite ir stebėkite skaitiklius.
Perpiešimų žemėlapis
Tie patys keturi laukai, perskaityti dviem būdais. Pakeiskite lauką ir skaičiuokite perpiešimus.
- Vardas
- Tema
- Skaitiklis
- Juodraštis
- Vardas
- Tema
- Skaitiklis
- Juodraštis
Sujungti įrašymai
autoSave laukia rašymo pauzės ir tada įrašo vieną kartą. Iš eilės einantys save() kvietimai dalijasi vienu eilėje laukiančiu įrašymu.
persistedRestate('', { key: 'note', autoSave: { delayMs: 400 } });Skaičiai gauti iš tikrų React perpiešimų ir saugyklos adapterio, skaičiuojančio įrašymus šiame puslapyje. Kitos bibliotekos taip pat gali pasirinkti reikšmes; čia pasirinkimas yra tik skaitymo būdas.
03 – Stabilu
Kai saugykla sugenda, jūsų duomenys lieka vietoje.
Būsena prarandama būtent saugant: lėti diskai, užblokuota saugykla, uždaryti skirtukai. Tai restate 1.0 garantijos, o ne patarimai.
- 01
Išsaugojimai vykdomi eilės tvarka
Kiekvienas store saugyklos operacijas atlieka po vieną, todėl lėtas išsaugojimas niekada neleidžia senai reikšmei perrašyti naujesnės.
save() · restore() - 02
Laukiantis išsaugojimas paima naujausią reikšmę
Iškvietus save(), kol eilėje laukia kitas išsaugojimas, jis prisijungia prie jo, o ne kaupia papildomus įrašymus.
save(): Promise<T> - 03
Neperskaityti įrašai niekada nepakeičiami per klaidą
Jei įrašas neįsikėlė, autoSave ir masiniai išsaugojimai jo neliečia ir praneša LOAD_FAILED. Pakeisti jį gali tik aiškus save().
LOAD_FAILED - 04
Išsaugojimas baigiamas ir po atjungimo
Prieš atjungimą paprašyti išsaugojimai baigiami. Vėlesni įrašymai į sunaikintą store ignoruojami, kaip React būsena po atjungimo.
useRestateScope - 05
Nėra saugyklos? Programa vis tiek paleidžiama
Serverio atvaizdavimo metu arba kai saugykla užblokuota, store toliau veikia atmintyje ir praneša, kad saugykla nepasiekiama.
status().available - 06
Klaidos pasako, kas ir kur
Kiekviena restate klaida turi tipizuotą kodą ir savo įrašo key, kad galėtumėte parodyti tinkamą pranešimą.
error.code · error.key
Auga kartu su jūsų programa
Pakeiskite aprašą. Komponentus palikite.
Išsaugojimas, automatinis išsaugojimas ir patikra yra to paties store parinktys. Komponentas dešinėje niekada nesikeičia.
Atmintis
Bendra reikšmė atmintyje.
import { restate } from '@kkwaenggwari/restate'; export const useDraft = restate('');Išsaugotas
Atkuriamas automatiškai; įrašomas, kai iškviečiate save().
import { persistedRestate } from '@kkwaenggwari/restate'; export const useDraft = persistedRestate('', { key: 'draft' });Automatinis išsaugojimas
Įrašoma vieną kartą po 500 ms rašymo pauzės.
import { persistedRestate } from '@kkwaenggwari/restate'; export const useDraft = persistedRestate('', { key: 'draft', autoSave: { delayMs: 500 }, });Su patikra
Senesni įrašai prieš naudojimą perkeliami ir patikrinami.
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', });
Komponentas – tas pats kiekviename žingsnyje
function Editor() {
const text = useDraft();
return <textarea value={text} onChange={(event) => useDraft.set(event.target.value)} />;
}Veikia visur, kur veikia React
Vienas paketas kiekvienai React aplinkai.
react >=16.8.0React
Veikia su React 16.8 ir naujesnėmis versijomis, naršyklėje ir serverio atvaizdavimo metu.
react-native-mmkv >=2.0.0React Native
Tie patys importai. Išsaugojimas naudoja MMKV vos įdiegus react-native-mmkv.
SSRServerio atvaizdavimas
Komponentų store paima serverio momentinę kopiją iš props ir pradeda įkėlimą po hidratacijos.
index.d.tsTypeScript
Deklaracijos pridėtos. Selektoriai, būsenos laukai ir klaidų kodai yra tipizuoti.
Pirmieji žingsniai
Jūsų pirmoji bendra būsena per minutę.
Įdiekite, aprašykite, naudokite. Išsaugojimą pridėkite, kai jo prireiks programai.
- 01Diegimas
npm install @kkwaenggwari/restate - 02Aprašykite store
export const useCart = restate({ items: 0, total: 0 }); - 03Skaitykite jį bet kuriame komponente
const items = useCart((cart) => cart.items);
KKWAENGGWARI