restate / v1.0.0
Ne s’abonner qu’au nécessaire
Choisissez la partie d’un store dont dépend chaque composant, comparez des objets avec shallow, regroupez les changements avec batch et branchez des événements externes.
Sur cette page
Passer un selectorEssayerSélectionner plusieurs champs avec shallowÉcrire votre propre comparaisonRegrouper les changements avec batchS’abonner en dehors de ReactBrancher des événements externesPasser un selector
Appelez le hook avec une fonction qui choisit ce dont le composant a besoin. Après chaque changement, restate exécute le selector et compare le résultat au précédent avec Object.is. S’ils sont égaux, le composant ne se rend pas.
Le message change. ItemCount se rend-il ?
Non. Son selector renvoie toujours le même nombre d’articles : la sélection est égale, donc React n’est pas sollicité pour le rendre.
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>;
}Essayer
Modifiez des champs et observez les compteurs de rendus. Désactivez shallow pour voir un selector qui renvoie un nouvel objet à chaque fois.
Préparation de l’exemple…
Sélectionner plusieurs champs avec shallow
Un selector qui construit un objet en renvoie un nouveau à chaque fois, donc Object.is voit toujours un changement. Passez shallow en second argument : il compare les champs propres de l’objet sur un niveau.
shallow compare aussi les entrées des tableaux, des Map et des Set sur un niveau.
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>
);
}Écrire votre propre comparaison
N’importe quelle fonction (left, right) => boolean convient comme second argument. Utilisez-la quand shallow ne suffit pas, par exemple pour comparer une liste par ids.
const ids = useCart(
(cart) => cart.items.map((item) => item.id),
(left, right) => left.length === right.length && left.every((id, index) => id === right[index]),
);Regrouper les changements avec batch
Chaque set notifie les abonnés immédiatement. Quand une action modifie plusieurs stores, enveloppez-la dans batch : la mémoire se met à jour tout de suite, et les abonnés s’exécutent une fois après le callback.
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.
}S’abonner en dehors de React
subscribeSelector ne vous appelle que lorsque la valeur sélectionnée change, avec la même fonction de comparaison facultative.
const stop = useCart.subscribeSelector(
(cart) => cart.items.length,
(count, previous) => analytics.track('cart-size', { count, previous }),
);Brancher des événements externes
Le second argument de restate peut être un updater. Il démarre quand le premier composant ou abonné commence à écouter, et son nettoyage s’exécute quand le dernier s’arrête : les événements du navigateur, les sockets ou les capteurs ne sont connectés que lorsque c’est nécessaire.
Utilisez start() et stop() pour le piloter à la main.
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);
};
});