TypeScript issue with inferred types in managed runtime context using React
Hello, how do you type your context when using a managed runtime as a ref? EDIT: fixed!
const createRuntime = async <T>(layer: Layer.Layer<T>) => {
const scope = Effect.runSync(Scope.make());
const runtime = await Effect.runPromise(
pipe(layer, Layer.toRuntime, Scope.extend(scope))
);
const runSync = Runtime.runSync(runtime);
const dispose = () => Effect.runFork(Scope.close(scope, Exit.void));
return { dispose, runSync };
};
const useEffectRuntime = <T>(layer: Layer.Layer<T>) => {
const create = React.useCallback(() => createRuntime(layer), []);
const ref = React.useRef<Awaited<ReturnType<typeof create>> | null>(null);
React.useEffect(() => {
const runtime = create();
void runtime.then((r) => (ref.current = r));
return () => {
void runtime.then((r) => r.dispose());
};
}, []);
return ref;
};
type Options = {
capacity: number;
delay: number;
type: 'sliding' | 'dropping' | 'bounded';
};
const Throttler = <T>() =>
class Throttler extends Context.Tag('Throttler')<
Throttler,
Queue.Queue<T>
>() {};
const take = <T>(delay: number) =>
pipe(
Effect.gen(function* () {
const queue = yield* Throttler<T>();
const item = yield* Queue.take(queue);
console.log(item);
yield* Effect.sleep(delay);
})
);
const layer = <T>(options: Options) =>
pipe(
Layer.scopedDiscard(
pipe(take<T>(options.delay), Effect.forever, Effect.forkScoped)
),
Layer.provideMerge(
Layer.scoped(Throttler<T>(), Queue[options.type]<T>(options.capacity))
)
);
export const useEffectQueue = <T>(options: Partial<Options>) => {
const runtimeRef = useEffectRuntime(layer<T>(options));
const enqueue = React.useCallback((value: T) => {
void runtimeRef.current?.runSync(
pipe(
Effect.gen(function* () {
return yield* Throttler<T>();
}),
Effect.andThen(Queue.offer(value))
)
);
}, []);
return enqueue;
};const createRuntime = async <T>(layer: Layer.Layer<T>) => {
const scope = Effect.runSync(Scope.make());
const runtime = await Effect.runPromise(
pipe(layer, Layer.toRuntime, Scope.extend(scope))
);
const runSync = Runtime.runSync(runtime);
const dispose = () => Effect.runFork(Scope.close(scope, Exit.void));
return { dispose, runSync };
};
const useEffectRuntime = <T>(layer: Layer.Layer<T>) => {
const create = React.useCallback(() => createRuntime(layer), []);
const ref = React.useRef<Awaited<ReturnType<typeof create>> | null>(null);
React.useEffect(() => {
const runtime = create();
void runtime.then((r) => (ref.current = r));
return () => {
void runtime.then((r) => r.dispose());
};
}, []);
return ref;
};
type Options = {
capacity: number;
delay: number;
type: 'sliding' | 'dropping' | 'bounded';
};
const Throttler = <T>() =>
class Throttler extends Context.Tag('Throttler')<
Throttler,
Queue.Queue<T>
>() {};
const take = <T>(delay: number) =>
pipe(
Effect.gen(function* () {
const queue = yield* Throttler<T>();
const item = yield* Queue.take(queue);
console.log(item);
yield* Effect.sleep(delay);
})
);
const layer = <T>(options: Options) =>
pipe(
Layer.scopedDiscard(
pipe(take<T>(options.delay), Effect.forever, Effect.forkScoped)
),
Layer.provideMerge(
Layer.scoped(Throttler<T>(), Queue[options.type]<T>(options.capacity))
)
);
export const useEffectQueue = <T>(options: Partial<Options>) => {
const runtimeRef = useEffectRuntime(layer<T>(options));
const enqueue = React.useCallback((value: T) => {
void runtimeRef.current?.runSync(
pipe(
Effect.gen(function* () {
return yield* Throttler<T>();
}),
Effect.andThen(Queue.offer(value))
)
);
}, []);
return enqueue;
};