@typed/fp
Version:
Data Structures and Resources for fp-ts
186 lines • 8.47 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.useKVs = exports.useKVStream = exports.useStream = exports.useStreamWith = exports.useReaderStream = exports.useReaderStreamWith = exports.bindEnvK = exports.useEnvK = exports.useWithPrevious = exports.useEffectWith = exports.useDisposable = exports.useDisposableWith = exports.useMemo = exports.useMemoWith = exports.useEq = exports.useEqWith = exports.defaultOptionRef = void 0;
const tslib_1 = require("tslib");
/**
* @typed/fp/Use is the only non-referentially transparent module in @typed/fp. It is built atop
* of [Ref](./Ref.ts.md) to enable many common workflows. If you're coming from a React background, it is
* pretty similar to hooks, but the only constraint is that is should be declared once at the top of the scope of your module.
* @since 0.11.0
*/
const disposable_1 = require("@most/disposable");
const function_1 = require("fp-ts/function");
const O = (0, tslib_1.__importStar)(require("fp-ts/Option"));
const Predicate_1 = require("fp-ts/Predicate");
const E = (0, tslib_1.__importStar)(require("./Env"));
const EO = (0, tslib_1.__importStar)(require("./EnvOption"));
const Eq_1 = require("./Eq");
const KV = (0, tslib_1.__importStar)(require("./KV"));
const RS = (0, tslib_1.__importStar)(require("./ReaderStream"));
const Ref = (0, tslib_1.__importStar)(require("./Ref"));
const RefDisposable = (0, tslib_1.__importStar)(require("./RefDisposable"));
const R = (0, tslib_1.__importStar)(require("./Resume"));
const Scheduler_1 = require("./Scheduler");
const S = (0, tslib_1.__importStar)(require("./Stream"));
/**
* Use Refs to check if a value has changed between invocations
* @since 0.11.0
* @category Constructor
*/
const defaultOptionRef = () => Ref.kv(E.of(O.none), Eq_1.alwaysEqualsEq);
exports.defaultOptionRef = defaultOptionRef;
/**
* Use Refs to check if a value has changed between invocations
* @since 0.11.0
* @category Combinator
*/
function useEqWith(ref) {
return (Eq = Eq_1.deepEqualsEq, initial = true) => (value) => (0, function_1.pipe)(E.Do, E.bindW('previous', () => ref.get), E.bindW('changed', ({ previous }) => (0, function_1.pipe)(previous, O.matchW(() => initial, (0, Predicate_1.not)(Eq.equals(value))), E.of)), E.chainW(({ previous, changed }) => (0, function_1.pipe)(previous, O.matchW(() => E.of(changed), () => (0, function_1.pipe)(value, O.some, ref.set, E.constant(changed))))));
}
exports.useEqWith = useEqWith;
/**
* Use Refs to check if a value has changed between invocations
* @since 0.11.0
* @category Combinator
*/
const useEq = (Eq = Eq_1.deepEqualsEq, initial = true) => useEqWith((0, exports.defaultOptionRef)())(Eq, initial);
exports.useEq = useEq;
/**
* @since 0.11.0
* @category Use
*/
const useMemoWith = (options) => (env, Eq = Eq_1.deepEqualsEq) => {
const changed = (0, function_1.pipe)(Eq, useEqWith(options.changed));
const updateRef = options.currentValue.update(() => EO.fromEnv(env));
return (0, function_1.flow)(changed, E.chainFirstW((changed) => (changed ? updateRef : E.of(null))), E.chainW(() => options.currentValue.get), EO.getOrElseEW(() => (0, function_1.pipe)(env, E.chainFirstW((0, function_1.flow)(O.some, options.currentValue.set)))));
};
exports.useMemoWith = useMemoWith;
const defaultUseMemoRefs = () => ({
currentValue: (0, exports.defaultOptionRef)(),
changed: (0, exports.defaultOptionRef)(),
});
/**
* @since 0.11.0
* @category Use
*/
const useMemo = (env, Eq = Eq_1.deepEqualsEq) => (0, exports.useMemoWith)(defaultUseMemoRefs())(env, Eq);
exports.useMemo = useMemo;
/**
* @since 0.11.0
* @category Use
*/
const useDisposableWith = (options) => (Eq = Eq_1.deepEqualsEq, switchLatest = false) => {
const changed = useEqWith(options.changed)(Eq);
return (f, value) => (0, function_1.pipe)(E.Do, E.bindW('changed', () => changed(value)), E.bindW('current', () => options.disposable.get), E.chainW(({ changed, current }) => changed
? (0, function_1.pipe)(E.fromIO(() => (switchLatest ? current.dispose() : null)), E.chainW(() => E.fromIO(f)), E.chainW((next) => (0, function_1.pipe)(next, RefDisposable.add, E.map((d) => (0, disposable_1.disposeBoth)(d, next)), E.chainW((a) => options.disposable.set(a)))))
: E.of(current)));
};
exports.useDisposableWith = useDisposableWith;
const defaultDisposableRefs = () => ({
disposable: Ref.kv(E.fromIO(disposable_1.disposeNone)),
changed: (0, exports.defaultOptionRef)(),
});
/**
* @since 0.11.0
* @category Use
*/
const useDisposable = (Eq = Eq_1.deepEqualsEq, switchLatest = false) => (0, exports.useDisposableWith)(defaultDisposableRefs())(Eq, switchLatest);
exports.useDisposable = useDisposable;
/**
* @since 0.11.0
* @category Use
*/
const useEffectWith = (options) => {
const useD = (0, exports.useDisposableWith)(options);
return (Eq = Eq_1.deepEqualsEq, switchLatest = false) => {
const use = useD(Eq, switchLatest);
return (env, value) => (0, function_1.pipe)(E.ask(), E.chainW((r) => use(() => (0, function_1.pipe)(r, (0, function_1.pipe)((0, Scheduler_1.delay)(0), E.chainW(() => env)), R.exec), value)));
};
};
exports.useEffectWith = useEffectWith;
/**
* @since 0.11.0
* @category Use
*/
const useWithPrevious = (ref) => {
return (f, value) => (0, function_1.pipe)(ref.get, E.map((previous) => f(previous, value)), E.chainFirstW(() => (0, function_1.pipe)(value, O.some, ref.set)));
};
exports.useWithPrevious = useWithPrevious;
/**
* Helps you to convert a Kliesli arrow of an Env into a function to
* a Disposable. Useful for UIs where you need to provide onClick={fn}
* style handlers.
* @since 0.11.0
* @category Use
*/
function useEnvK(f, onValue = E.of) {
return (0, function_1.pipe)(E.Do, E.apSW('refDisposable', RefDisposable.get), E.apSW('resumeF', E.toResumeK(f)), E.apSW('resumeV', E.toResumeK(onValue)), E.map(({ resumeF, resumeV, refDisposable }) => (...args) => {
const d1 = (0, function_1.pipe)(resumeF(...args), R.chain(resumeV), R.exec);
const d2 = refDisposable.addDisposable(d1);
return (0, disposable_1.disposeBoth)(d1, d2);
}));
}
exports.useEnvK = useEnvK;
/**
* @since 0.11.0
* @category Use
*/
const bindEnvK = (name, f, onValue) => (ma) => E.bindW(name, () => useEnvK(f, onValue))(ma);
exports.bindEnvK = bindEnvK;
/**
* @since 0.11.0
* @category Use
*/
const useReaderStreamWith = (options) => (Eq = Eq_1.deepEqualsEq) => {
const use = (0, exports.useDisposableWith)(options)(Eq);
return (rs, dep) => (0, function_1.pipe)(E.asksE((r) => use(() => rs(r).run(S.createSink({
event: (_, value) => (0, function_1.pipe)(value, O.some, options.value.set, E.execWith(r)),
}), r.scheduler), dep)), E.chainW(() => options.value.get));
};
exports.useReaderStreamWith = useReaderStreamWith;
const defaultUserReaderStreamRefs = () => ({
disposable: Ref.kv(E.fromIO(disposable_1.disposeNone)),
changed: (0, exports.defaultOptionRef)(),
value: (0, exports.defaultOptionRef)(),
});
/**
* @since 0.11.0
* @category Use
*/
const useReaderStream = (Eq = Eq_1.deepEqualsEq) => (0, exports.useReaderStreamWith)(defaultUserReaderStreamRefs())(Eq);
exports.useReaderStream = useReaderStream;
/**
* @since 0.11.0
* @category Use
*/
const useStreamWith = (options) => (Eq = Eq_1.deepEqualsEq) => {
const useRS = (0, function_1.pipe)(Eq, (0, exports.useReaderStreamWith)(options));
return (stream, dep) => useRS(() => stream, dep);
};
exports.useStreamWith = useStreamWith;
/**
* @since 0.11.0
* @category Use
*/
const useStream = (Eq = Eq_1.deepEqualsEq) => {
const use = (0, exports.useStreamWith)(defaultUserReaderStreamRefs())(Eq);
return (stream, dep) => use(stream, dep);
};
exports.useStream = useStream;
/**
* @since 0.11.0
* @category Use
*/
const useKVStream = (f, Eq) => {
const use = RS.fromEnv(KV.useKeyedEnvs(Eq_1.EqStrict));
const mergeMap = RS.mergeMapWhen(Eq_1.EqStrict);
return (stream) => (0, function_1.pipe)(use, RS.switchMapW(({ findRefs, deleteRefs }) => (0, function_1.pipe)(stream, RS.keyed(Eq), mergeMap((s) => (0, function_1.pipe)(s, RS.fromStream, RS.switchMapW(f), RS.onDispose(deleteRefs(s)), RS.useSomeWith(RS.fromEnv(findRefs(s))))))));
};
exports.useKVStream = useKVStream;
/**
* @since 0.11.0
* @category Use
*/
const useKVs = (f, Eq) => (0, exports.useKVStream)((0, function_1.flow)(f, KV.sample), Eq);
exports.useKVs = useKVs;
//# sourceMappingURL=Use.js.map