UNPKG

react-external-subject

Version:

Wrap mutable sources into React-safe mutable source

174 lines (170 loc) 4.4 kB
// src/create-external-subject.ts function defaultShouldUpdate(a, b) { return !Object.is(a, b); } function createExternalSubject(options) { var _a; const shouldUpdate = (_a = options.shouldUpdate) != null ? _a : defaultShouldUpdate; let cache; const listeners = new Set(); let request; const getCachedValue = () => { if (!cache) { cache = { value: options.read() }; } return cache.value; }; let synchronize; const setSynchronizer = (sync) => { if (!synchronize) { synchronize = sync; } }; const requestUpdate = () => { if (shouldUpdate(getCachedValue(), options.read())) { if (request) { request.alive = false; } request = { promise: Promise.resolve().then(() => new Promise((resolve) => { if (request == null ? void 0 : request.alive) { request = void 0; const onResolve = () => { if (cache) { cache.value = options.read(); } listeners.forEach((listener) => { listener(); }); resolve(); }; if (synchronize) { synchronize(onResolve); } else { onResolve(); } } })), alive: true }; } }; let unsubscribe; const subscribe = (handler) => { listeners.add(handler); if (options.lazySubscribe && options.subscribe && listeners.size === 1) { const unsub = options.subscribe(requestUpdate); if (unsub) { unsubscribe = unsub; } } return () => { listeners.delete(handler); if (options.lazySubscribe && unsubscribe) { unsubscribe(); } }; }; if (!options.lazySubscribe && options.subscribe) { unsubscribe = options.subscribe(requestUpdate); } const destroy = () => { if (request) { request.alive = false; } listeners.clear(); if (unsubscribe) { unsubscribe(); } }; return { subscribe, shouldUpdate, requestUpdate, getRequest: () => request, getCachedValue, getCurrentValue: options.read, setSynchronizer, destroy }; } // src/use-external-subject.ts import { useMemoCondition, useSubscription } from "@lyonph/react-hooks"; import {useDebugValue} from "react"; // src/ExternalSubjectSynchronizer.tsx import React, { createContext, Suspense, useContext, useEffect, useState } from "react"; import {useConstantCallback} from "@lyonph/react-hooks"; var ExternalSubjectSynchronizerContext = createContext(void 0); function useExternalSubjectSynchronize() { const context = useContext(ExternalSubjectSynchronizerContext); if (context) { return context; } throw new Error("Found no synchronizer."); } function ExternalSubjectSynchronizer({children}) { const [state, setState] = useState([]); const synchronize = useConstantCallback((cb) => { setState((current) => [...current, cb]); }); useEffect(() => { if (state.length) { setState([]); state.forEach((update) => { update(); }); } }, [state]); return /* @__PURE__ */ React.createElement(ExternalSubjectSynchronizerContext.Provider, { value: synchronize }, /* @__PURE__ */ React.createElement(Suspense, { fallback: null }, children)); } // src/use-external-subject.ts function useExternalSubjectInternal(subject, suspense = false) { subject.setSynchronizer(useExternalSubjectSynchronize()); const subscription = useMemoCondition(() => ({ read: () => subject.getCachedValue(), subscribe: (handler) => subject.subscribe(handler), shouldUpdate: (a, b) => subject.shouldUpdate(a, b) }), subject); const state = useSubscription(subscription); const ongoing = subject.getRequest(); if (ongoing) { if (suspense) { throw ongoing.promise; } } else { subject.requestUpdate(); const current = subject.getRequest(); if (current) { if (suspense) { throw current.promise; } } } return state; } function useExternalSubject(subject, suspense = false) { const value = useExternalSubjectInternal(subject, suspense); useDebugValue(value); return value; } export { ExternalSubjectSynchronizer, createExternalSubject, useExternalSubject }; //# sourceMappingURL=react-external-subject.esm.js.map