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 は常に変更ありと判断します。2 番目の引数に shallow を渡すと、オブジェクト自身のフィールドを 1 階層だけ比較します。

shallow は配列、Map、Set の要素も 1 階層だけ比較します。

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>
  );
}

独自の比較関数を書く

2 番目の引数には任意の関数 (left, right) => boolean を渡せます。リストを id で比較するなど、shallow では足りないときに使います。

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 はそのたびに購読者へ即座に通知します。1 つの操作で複数のストアを変えるときは batch で囲みます。メモリはすぐに更新され、購読者はコールバックの後に 1 回だけ実行されます。

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 の 2 番目の引数には updater を渡せます。最初のコンポーネントや購読者が購読を始めたときに開始し、最後の 1 つが購読をやめるとクリーンアップが実行されるので、ブラウザーのイベント、ソケット、センサーは必要な間だけ接続されます。

手動で制御するには 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);
  };
});

ドキュメントを検索

すべてのガイドのタイトル、説明、本文から検索します。