restate / v1.0.0
필요한 것만 구독하기
컴포넌트마다 의존하는 스토어의 일부를 고르고, 객체는 shallow로 비교하고, batch로 변경을 묶고, 외부 이벤트를 연결합니다.
selector 넘기기
컴포넌트가 필요한 것을 고르는 함수를 훅에 넘기세요. 변경이 일어나면 restate가 selector를 실행하고 결과를 이전 결과와 Object.is로 비교합니다. 같다면 컴포넌트는 렌더하지 않습니다.
메모가 바뀌었습니다. ItemCount는 렌더할까요?
아닙니다. selector가 여전히 같은 상품 수를 반환하므로 선택값이 같고, React에 렌더를 요청하지 않습니다.
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의 항목도 한 단계 깊이로 비교합니다.
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 함수를 넘길 수 있습니다. 목록을 id로 비교하듯 shallow로 부족할 때 쓰세요.
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로 감싸세요. 메모리는 즉시 바뀌고, 구독자는 콜백이 끝난 뒤 한 번 실행됩니다.
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는 선택한 값이 바뀔 때만 호출하며, 같은 비교 함수를 선택적으로 받습니다.
const stop = useCart.subscribeSelector(
(cart) => cart.items.length,
(count, previous) => analytics.track('cart-size', { count, previous }),
);외부 이벤트 연결하기
restate의 두 번째 인자로 updater를 넘길 수 있습니다. 첫 컴포넌트나 구독자가 듣기 시작할 때 시작하고, 마지막 구독이 끝나면 정리 함수가 실행됩니다. 브라우저 이벤트, 소켓, 센서를 필요할 때만 연결합니다.
start()와 stop()으로 직접 제어할 수도 있습니다.
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);
};
});