restate / v1.0.0

Подписывайтесь только на нужное

Выбирайте, от какой части хранилища зависит каждый компонент, сравнивайте объекты через shallow, группируйте изменения через batch и подключайте внешние события.

На этой страницеПередайте selectorПопробуйтеВыбор нескольких полей через shallowСвоя функция сравненияГруппировка изменений через batchПодписка вне ReactПодключение внешних событий

Передайте selector

Вызовите хук с функцией, которая выбирает то, что нужно компоненту. После каждого изменения restate выполняет selector и сравнивает результат с предыдущим через Object.is. Если они равны, компонент не перерисовывается.

Открытка меняется. Перерисуется ли ItemCount?

Нет. Его selector по-прежнему возвращает то же количество товаров, поэтому выборка не изменилась и React не просят его перерисовывать.

JavaScript / JSX
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>;
}

Попробуйте

Меняйте поля и следите за счётчиками рендеров. Выключите shallow, чтобы увидеть selector, который каждый раз возвращает новый объект.

Подготовка примера…

Выбор нескольких полей через shallow

Если selector собирает объект, он каждый раз возвращает новый, поэтому Object.is всегда видит изменение. Передайте shallow вторым аргументом: он сравнивает собственные поля объекта на один уровень вглубь.

shallow сравнивает на один уровень и элементы массивов, Map и Set.

JavaScript / JSX
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>
  );
}

Своя функция сравнения

Вторым аргументом подойдёт любая функция (left, right) => boolean. Используйте её, когда shallow недостаточно, например чтобы сравнивать список по id.

JavaScript / JSX
const ids = useCart(
  (cart) => cart.items.map((item) => item.id),
  (left, right) => left.length === right.length && left.every((id, index) => id === right[index]),
);

Группировка изменений через batch

Каждый set сразу уведомляет подписчиков. Когда одно действие меняет несколько хранилищ, оберните его в batch: память обновляется сразу, а подписчики срабатывают один раз после колбэка.

JavaScript / JSX
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.
}

Подписка вне React

subscribeSelector вызывает вас, только когда меняется выбранное значение, с той же необязательной функцией сравнения.

JavaScript / JSX
const stop = useCart.subscribeSelector(
  (cart) => cart.items.length,
  (count, previous) => analytics.track('cart-size', { count, previous }),
);

Подключение внешних событий

Вторым аргументом restate может быть updater. Он запускается, когда начинает слушать первый компонент или подписчик, а его очистка выполняется, когда перестаёт последний, — так события браузера, сокеты и датчики подключены только пока нужны.

Для ручного управления используйте start() и stop().

JavaScript / JSX
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);
  };
});

Поиск по документации

Поиск по заголовкам, описаниям и тексту всех руководств.