restate / v1.0.0
Nur abonnieren, was du brauchst
Wähle den Teil eines Stores, von dem jede Komponente abhängt, vergleiche Objekte mit shallow, bündle Änderungen mit batch und verbinde externe Events.
Auf dieser Seite
Einen selector übergebenAusprobierenMehrere Felder mit shallow auswählenEinen eigenen Vergleich schreibenÄnderungen mit batch bündelnAußerhalb von React abonnierenExterne Events verbindenEinen selector übergeben
Ruf den Hook mit einer Funktion auf, die auswählt, was die Komponente braucht. Nach jeder Änderung führt restate den selector aus und vergleicht das Ergebnis per Object.is mit dem vorherigen. Sind beide gleich, rendert die Komponente nicht.
Die Nachricht ändert sich. Rendert ItemCount?
Nein. Sein selector gibt weiterhin dieselbe Artikelanzahl zurück, die Auswahl ist also gleich und React wird nicht um ein Rendern gebeten.
const useCart = restate({ items: ['Book'], coupon: '', note: '' });
function ItemCount() {
// Re-renders only when the number of items changes.
const count = useCart((cart) => cart.items.length);
return <p>{count} items</p>;
}Ausprobieren
Ändere Felder und beobachte die Render-Zähler. Schalte shallow aus, um einen selector zu sehen, der jedes Mal ein neues Objekt zurückgibt.
Beispiel wird vorbereitet…
Mehrere Felder mit shallow auswählen
Ein selector, der ein Objekt baut, gibt jedes Mal ein neues zurück, also sieht Object.is immer eine Änderung. Übergib shallow als zweites Argument: Es vergleicht die eigenen Felder des Objekts eine Ebene tief.
shallow vergleicht auch Einträge von Arrays, Map und Set eine Ebene tief.
import { restate, shallow } from '@kkwaenggwari/restate';
function CartSummary() {
const { count, coupon } = useCart(
(cart) => ({ count: cart.items.length, coupon: cart.coupon }),
shallow,
);
return (
<p>
{count} items · {coupon || 'no coupon'}
</p>
);
}Einen eigenen Vergleich schreiben
Jede Funktion (left, right) => boolean funktioniert als zweites Argument. Nutze sie, wenn shallow nicht reicht, etwa um eine Liste nach ids zu vergleichen.
const ids = useCart(
(cart) => cart.items.map((item) => item.id),
(left, right) => left.length === right.length && left.every((id, index) => id === right[index]),
);Änderungen mit batch bündeln
Jedes set benachrichtigt Abonnenten sofort. Wenn eine Aktion mehrere Stores ändert, umschließe sie mit batch: Der Arbeitsspeicher aktualisiert sich sofort, und Abonnenten laufen nach dem Callback einmal.
import { batch } from '@kkwaenggwari/restate';
function checkout() {
batch(() => {
useCart.set((cart) => ({ ...cart, items: [] }));
useOrder.set((order) => ({ ...order, status: 'paid' }));
});
// Subscribers run once, after both stores changed.
}Außerhalb von React abonnieren
subscribeSelector ruft dich nur auf, wenn sich der ausgewählte Wert ändert – mit derselben optionalen Vergleichsfunktion.
const stop = useCart.subscribeSelector(
(cart) => cart.items.length,
(count, previous) => analytics.track('cart-size', { count, previous }),
);Externe Events verbinden
Das zweite Argument von restate kann ein updater sein. Er startet, wenn die erste Komponente oder der erste Abonnent zuzuhören beginnt, und sein Cleanup läuft, wenn der letzte aufhört – so sind Browser-Events, Sockets oder Sensoren nur verbunden, solange sie gebraucht werden.
Mit start() und stop() steuerst du ihn von Hand.
import { restate } from '@kkwaenggwari/restate';
// Listens while at least one component or subscriber uses the store.
export const useOnline = restate(true, ({ setState }) => {
const update = () => setState(navigator.onLine);
window.addEventListener('online', update);
window.addEventListener('offline', update);
update();
return () => {
window.removeEventListener('online', update);
window.removeEventListener('offline', update);
};
});