restate / v1.0.0

Σύγκριση με άλλα εργαλεία

Οι ίδιες εργασίες γραμμένες με Redux Toolkit, Zustand, Jotai και restate, πώς μετρώνται οι αριθμοί και πότε ταιριάζει καλύτερα άλλο εργαλείο.

Σε αυτή τη σελίδαΠώς συγκρίνουμεΊδια εργασία, τέσσερις τρόποιΠότε ταιριάζει καλύτερα άλλο εργαλείο

Πώς συγκρίνουμε

Κάθε έκδοση φτιάχνει την ίδια οθόνη με τις ίδιες καταστάσεις, την ίδια αποθήκευση και τον ίδιο χειρισμό σφαλμάτων. Τίποτα δεν παραλείπεται σε καμία πλευρά για να φαίνεται συντομότερη.

Και άλλες βιβλιοθήκες επιλέγουν τιμές, λειτουργούν χωρίς Provider ή αποθηκεύουν κατάσταση. Η σύγκριση δείχνει πόσος κώδικας χρειάζεται για το ίδιο αποτέλεσμα, όχι ποιες δυνατότητες υπάρχουν.

Ίδια εργασία, τέσσερις τρόποι

Επιλέξτε εργασία και βιβλιοθήκη. Οι γραμμές και τα API υπολογίζονται από τον κώδικα που φαίνεται κατά το build του ιστότοπου.

Κοινός μετρητής

Δύο components διαβάζουν και ενημερώνουν τον ίδιο αριθμό.

Redux Toolkit Οργανώνει τις ενημερώσεις σε slices και actions που αποστέλλονται σε ένα store διαθέσιμο μέσω του context του React. Εξαιρετικά DevTools και αυστηρή, προβλέψιμη ροή ενημερώσεων για μεγάλες ομάδες.

Zustand Δημιουργεί ένα hook store με create() και το αλλάζει με setState. Μικροσκοπικό και ευέλικτο, με middleware για μόνιμη αποθήκευση και DevTools.

Jotai Συνθέτει atoms και παράγωγα atoms, προαιρετικά μέσα σε Provider. Λεπτομερείς εξαρτήσεις ανάμεσα σε πολλά μικρά κομμάτια κατάστασης.

Redux Toolkit29 γραμμές · 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 γραμμές · 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 γραμμές · 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 Μία δήλωση είναι ταυτόχρονα store και hook.

restate13 γραμμές · 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 />
    </>
  );
}

Για αυτή την εργασία, και τα Zustand και Jotai είναι μικρά και χωρίς Provider. Η διαφορά μεγαλώνει όταν προστίθενται αποθήκευση και κατάσταση ανά έγγραφο.

Επιλογή πεδίου

Το component τίτλου δεν πρέπει να αποδίδεται όταν αλλάζει μόνο το κείμενο.

Redux Toolkit Οργανώνει τις ενημερώσεις σε slices και actions που αποστέλλονται σε ένα store διαθέσιμο μέσω του context του React. Εξαιρετικά DevTools και αυστηρή, προβλέψιμη ροή ενημερώσεων για μεγάλες ομάδες.

Zustand Δημιουργεί ένα hook store με create() και το αλλάζει με setState. Μικροσκοπικό και ευέλικτο, με middleware για μόνιμη αποθήκευση και DevTools.

Jotai Συνθέτει atoms και παράγωγα atoms, προαιρετικά μέσα σε Provider. Λεπτομερείς εξαρτήσεις ανάμεσα σε πολλά μικρά κομμάτια κατάστασης.

Redux Toolkit26 γραμμές · 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 γραμμές · 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 γραμμές · 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 Δώστε έναν selector στο ίδιο hook που ήδη χρησιμοποιείτε.

restate17 γραμμές · 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>
    </>
  );
}

Όλα τα εργαλεία εδώ μπορούν να επιλέγουν. Το restate χρησιμοποιεί την ίδια κλήση hook αντί για ξεχωριστό hook selector ή παράγωγο atom.

Αποθήκευση πρόχειρου

Ανακτήστε ένα αποθηκευμένο πρόχειρο κατά την εκκίνηση, αποθηκεύστε με κλικ και δείξτε τις καταστάσεις αποθήκευσης σε εξέλιξη, μη αποθηκευμένο και αποτυχία.

Redux Toolkit Οργανώνει τις ενημερώσεις σε slices και actions που αποστέλλονται σε ένα store διαθέσιμο μέσω του context του React. Εξαιρετικά DevTools και αυστηρή, προβλέψιμη ροή ενημερώσεων για μεγάλες ομάδες.

Zustand Δημιουργεί ένα hook store με create() και το αλλάζει με setState. Μικροσκοπικό και ευέλικτο, με middleware για μόνιμη αποθήκευση και DevTools.

Jotai Συνθέτει atoms και παράγωγα atoms, προαιρετικά μέσα σε Provider. Λεπτομερείς εξαρτήσεις ανάμεσα σε πολλά μικρά κομμάτια κατάστασης.

Redux Toolkit84 γραμμές · 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 γραμμές · 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 γραμμές · 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 ανακτά αυτόματα και το useStatus αναφέρει αποθήκευση, μη αποθηκευμένες αλλαγές και σφάλματα.

restate18 γραμμές · 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>
    </>
  );
}

Οι άλλες εκδόσεις γράφουν με το χέρι τον ίδιο κώδικα IndexedDB. Τα middleware μόνιμης αποθήκευσης ή τα εργαλεία αποθήκευσής τους μπορούν να τον συντομεύσουν με έναν προσαρμογέα· αυτός ο κώδικας δεν μετράται εδώ.

Κατάσταση ανά έγγραφο

Δύο έγγραφα κρατούν ανεξάρτητη κατάσταση, και το κλείσιμο του ενός την απελευθερώνει.

Redux Toolkit Οργανώνει τις ενημερώσεις σε slices και actions που αποστέλλονται σε ένα store διαθέσιμο μέσω του context του React. Εξαιρετικά DevTools και αυστηρή, προβλέψιμη ροή ενημερώσεων για μεγάλες ομάδες.

Zustand Δημιουργεί ένα hook store με create() και το αλλάζει με setState. Μικροσκοπικό και ευέλικτο, με middleware για μόνιμη αποθήκευση και DevTools.

Jotai Συνθέτει atoms και παράγωγα atoms, προαιρετικά μέσα σε Provider. Λεπτομερείς εξαρτήσεις ανάμεσα σε πολλά μικρά κομμάτια κατάστασης.

Redux Toolkit42 γραμμές · 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 γραμμές · 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 γραμμές · 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 δημιουργεί stores και τα καταστρέφει μετά την αποπροσάρτηση.

restate27 γραμμές · 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" />
    </>
  );
}

Ένας Provider ή ένα store στο Jotai και τα vanilla stores στο Zustand μπορούν επίσης να απομονώσουν κατάσταση· τα δημιουργείτε και τα συνδέετε εσείς.

Πώς μετρώνται αυτοί οι αριθμοί

Οι μη κενές γραμμές του κώδικα που φαίνεται και τα ονόματα που εισάγονται από κάθε βιβλιοθήκη κατάστασης (τα imports του React δεν μετρώνται). Κάθε έκδοση αποδίδει το ίδιο UI με την ίδια αποθήκευση και τις ίδιες καταστάσεις φόρτωσης και σφάλματος. Τα παραδείγματα restate υποθέτουν await restateManager.ready στο σημείο εισόδου της εφαρμογής. Οι πηγές ελέγχθηκαν με την επίσημη τεκμηρίωση κάθε έργου:

  • Redux Toolkit — Redux Toolkit · Quick Start, React Redux · useSelector, Redux Toolkit · configureStore. Επιλέξτε το όταν μετράει περισσότερο ένα ελέγξιμο αρχείο actions και ένα μεγάλο οικοσύστημα middleware
  • Zustand — Zustand · Introduction, Zustand · persist. Επιλέξτε το όταν θέλετε τον μικρότερο δυνατό πυρήνα και προτιμάτε να συνθέτετε middleware
  • Jotai — Jotai · atom, Jotai · Storage, Jotai · Provider. Επιλέξτε το όταν η κατάστασή σας είναι εκ φύσεως γράφος από μικρές παράγωγες τιμές

Πότε ταιριάζει καλύτερα άλλο εργαλείο

Το restate είναι για κατάσταση εφαρμογής που μοιράζονται τα components και μερικές φορές αποθηκεύουν. Άλλα εργαλεία μπορεί να ταιριάζουν καλύτερα:

  • Redux Toolkit, όταν θέλετε κάθε αλλαγή να περνά από actions και reducers, με αποσφαλμάτωση ταξιδιού στον χρόνο.
  • Zustand, όταν θέλετε τον μικρότερο πυρήνα και να χτίσετε τη μόνιμη αποθήκευση με middleware της επιλογής σας.
  • Jotai, όταν η κατάστασή σας είναι γράφος από πολλές μικρές παράγωγες τιμές.
  • Βιβλιοθήκες δεδομένων διακομιστή, όταν τα δεδομένα ζουν κυρίως στον διακομιστή και χρειάζονται cache και επανάκτηση.

Αναζήτηση στην τεκμηρίωση

Αναζήτηση σε τίτλους, περιγραφές και περιεχόμενο όλων των οδηγών.