Effect CommunityEC
Effect Community2y ago
5 replies
donverduyn

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;
};
Was this page helpful?