restate / v1.0.0
Subskrybuj tylko to, czego potrzebujesz
Wybierz, od której części magazynu zależy każdy komponent, porównuj obiekty przez shallow, grupuj zmiany przez batch i podłączaj zdarzenia zewnętrzne.
Na tej stronie
Przekaż selectorWypróbujWybieraj kilka pól przez shallowNapisz własne porównanieGrupuj zmiany przez batchSubskrybuj poza ReactPodłącz zdarzenia zewnętrznePrzekaż selector
Wywołaj hook z funkcją, która wybiera to, czego potrzebuje komponent. Po każdej zmianie restate uruchamia selector i porównuje wynik z poprzednim przez Object.is. Jeśli są równe, komponent się nie renderuje.
Kartka się zmienia. Czy ItemCount się wyrenderuje?
Nie. Jego selector wciąż zwraca tę samą liczbę produktów, więc wybór jest równy i React nie jest proszony o render.
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>;
}Wypróbuj
Zmieniaj pola i obserwuj liczniki renderowań. Wyłącz shallow, by zobaczyć selector, który za każdym razem zwraca nowy obiekt.
Przygotowywanie przykładu…
Wybieraj kilka pól przez shallow
Jeśli selector buduje obiekt, za każdym razem zwraca nowy, więc Object.is zawsze widzi zmianę. Przekaż shallow jako drugi argument: porównuje własne pola obiektu na jednym poziomie głębokości.
shallow porównuje na jednym poziomie także elementy tablic, Map i Set.
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>
);
}Napisz własne porównanie
Jako drugi argument nada się dowolna funkcja (left, right) => boolean. Użyj jej, gdy shallow nie wystarcza, na przykład do porównania listy po id.
const ids = useCart(
(cart) => cart.items.map((item) => item.id),
(left, right) => left.length === right.length && left.every((id, index) => id === right[index]),
);Grupuj zmiany przez batch
Każde set od razu powiadamia subskrybentów. Gdy jedna akcja zmienia kilka magazynów, owiń ją w batch: pamięć aktualizuje się od razu, a subskrybenci uruchamiają się raz po callbacku.
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.
}Subskrybuj poza React
subscribeSelector wywołuje cię tylko wtedy, gdy zmienia się wybrana wartość, z tą samą opcjonalną funkcją porównania.
const stop = useCart.subscribeSelector(
(cart) => cart.items.length,
(count, previous) => analytics.track('cart-size', { count, previous }),
);Podłącz zdarzenia zewnętrzne
Drugim argumentem restate może być updater. Startuje, gdy pierwszy komponent lub subskrybent zaczyna nasłuchiwać, a jego sprzątanie uruchamia się, gdy przestaje ostatni, więc zdarzenia przeglądarki, gniazda czy czujniki są podłączone tylko wtedy, gdy są potrzebne.
Użyj start() i stop(), by sterować nim ręcznie.
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);
};
});