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.

Terminal
npm install @kkwaenggwari/restate

Gyvai – 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.

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ę.

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ų.

Redux Toolkit29 eilutės · 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 eilutės · 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 eilutės · 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 Vienas aprašas yra ir store, ir hook.

restate13 eilutės · 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 />
    </>
  );
}

Š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ų.

Redux Toolkit26 eilutės · 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 eilutės · 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 eilutės · 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 Perduokite selector tam pačiam hook, kurį jau naudojate.

restate17 eilutės · 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>
    </>
  );
}

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ų.

Redux Toolkit84 eilutės · 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 eilutės · 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 eilutės · 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 atkuria automatiškai, o useStatus praneša apie išsaugojimą, neišsaugotus pakeitimus ir klaidas.

restate18 eilutės · 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>
    </>
  );
}

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ų.

Redux Toolkit42 eilutės · 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 eilutės · 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 eilutės · 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 sukuria store ir juos sunaikina po atjungimo.

restate27 eilutės · 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" />
    </>
  );
}

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.

Skaito visą store
  • Vardas
  • Tema
  • Skaitiklis
  • Juodraštis
Pasirenka vieną lauką
  • 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.

Atidaryti gedimų laboratoriją Skaityti patikimumo vadovą
  1. 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()
  2. 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>
  3. 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
  4. 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
  5. 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
  6. 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.

  1. Atmintis

    Bendra reikšmė atmintyje.

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

    Atkuriamas automatiškai; įrašomas, kai iškviečiate save().

    import { persistedRestate } from '@kkwaenggwari/restate';
    
    export const useDraft = persistedRestate('', { key: 'draft' });
  3. 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 },
    });
  4. 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.0

    React

    Veikia su React 16.8 ir naujesnėmis versijomis, naršyklėje ir serverio atvaizdavimo metu.

  • react-native-mmkv >=2.0.0

    React Native

    Tie patys importai. Išsaugojimas naudoja MMKV vos įdiegus react-native-mmkv.

  • SSR

    Serverio atvaizdavimas

    Komponentų store paima serverio momentinę kopiją iš props ir pradeda įkėlimą po hidratacijos.

  • index.d.ts

    TypeScript

    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.

  1. 01
    Diegimas
    Terminal
    npm install @kkwaenggwari/restate
  2. 02
    Aprašykite store
    JavaScript / JSX
    export const useCart = restate({ items: 0, total: 0 });
  3. 03
    Skaitykite jį bet kuriame komponente
    JavaScript / JSX
    const items = useCart((cart) => cart.items);

KKWAENGGWARI

Projektai

Ieškoti dokumentacijoje

Ieškokite visų vadovų pavadinimuose, aprašymuose ir tekste.