From c689778cea295dea48cf95ec99406b8668b4bbef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 9 Mar 2025 18:08:52 +0100 Subject: [PATCH] Working query --- .../example/src/routes/query/usequery.tsx | 2 +- .../extension-query/src/QueryExtension.ts | 22 ++++---- packages/extension-query/src/QueryRunner.ts | 55 ++++++++++++------- packages/extension-query/src/QueryService.ts | 4 +- 4 files changed, 49 insertions(+), 34 deletions(-) diff --git a/packages/example/src/routes/query/usequery.tsx b/packages/example/src/routes/query/usequery.tsx index 7fe1a35..1f76770 100644 --- a/packages/example/src/routes/query/usequery.tsx +++ b/packages/example/src/routes/query/usequery.tsx @@ -21,7 +21,7 @@ function RouteComponent() { const query = R.useQuery({ key: R.useStreamFromValues(["uuid4", count]), - query: () => Console.log(`Querying ${ count } IDs...`).pipe( + query: ([, count]) => Console.log(`Querying ${ count } IDs...`).pipe( Effect.andThen(Effect.sleep("500 millis")), Effect.andThen(HttpClient.get(`https://www.uuidtools.com/api/generate/v4/count/${ count }`)), HttpClient.withTracerPropagation(false), diff --git a/packages/extension-query/src/QueryExtension.ts b/packages/extension-query/src/QueryExtension.ts index 8b0c6dd..0d0cccd 100644 --- a/packages/extension-query/src/QueryExtension.ts +++ b/packages/extension-query/src/QueryExtension.ts @@ -1,5 +1,5 @@ import * as AsyncData from "@typed/async-data" -import { Context, Effect, Fiber, Layer, Ref, Stream, SubscriptionRef } from "effect" +import { type Cause, Context, Effect, Fiber, Layer, Stream, SubscriptionRef } from "effect" import * as React from "react" import { ReffuseExtension, type ReffuseHelpers } from "reffuse" import * as QueryRunner from "./QueryRunner.js" @@ -14,7 +14,7 @@ export interface UseQueryProps { export interface UseQueryResult { readonly state: SubscriptionRef.SubscriptionRef> - readonly refresh: Effect.Effect> + readonly refresh: Effect.Effect> readonly layer: ( tag: Context.TagClass> ) => Layer.Layer @@ -28,16 +28,18 @@ export const QueryExtension = ReffuseExtension.make(() => ({ ): UseQueryResult { const runner = this.useMemo(() => QueryRunner.make({ key: props.key, - query: props.query(), + query: props.query, }), [props.key]) - this.useFork(() => Effect.addFinalizer(() => runner.forkInterrupt).pipe( - Effect.andThen(Stream.runForEach(runner.key, () => - Ref.set(runner.queryRef, props.query()).pipe( - Effect.andThen(runner.forkFetch) - ) - )) - ), [runner]) + // this.useFork(() => Effect.addFinalizer(() => runner.forkInterrupt).pipe( + // Effect.andThen(Stream.runForEach(runner.key, () => + // Ref.set(runner.queryRef, props.query()).pipe( + // Effect.andThen(runner.forkFetch) + // ) + // )) + // ), [runner]) + + this.useFork(() => runner.fetchOnKeyChange, [runner]) this.useFork(() => (props.refreshOnWindowFocus ?? true) ? runner.refreshOnWindowFocus diff --git a/packages/extension-query/src/QueryRunner.ts b/packages/extension-query/src/QueryRunner.ts index 106f7e4..2a517a4 100644 --- a/packages/extension-query/src/QueryRunner.ts +++ b/packages/extension-query/src/QueryRunner.ts @@ -1,6 +1,6 @@ import { BrowserStream } from "@effect/platform-browser" import * as AsyncData from "@typed/async-data" -import { Effect, Fiber, identity, Option, Ref, Stream, SubscriptionRef } from "effect" +import { type Cause, Effect, Fiber, identity, Option, Ref, Scope, Stream, SubscriptionRef } from "effect" export interface QueryRunner { @@ -8,13 +8,13 @@ export interface QueryRunner { readonly query: (key: K) => Effect.Effect readonly stateRef: SubscriptionRef.SubscriptionRef> - readonly fiberRef: SubscriptionRef.SubscriptionRef>> + readonly fiberRef: SubscriptionRef.SubscriptionRef>> - readonly interrupt: Effect.Effect - readonly forkInterrupt: Effect.Effect> - readonly forkFetch: Effect.Effect> - readonly forkRefresh: Effect.Effect> + readonly forkInterrupt: Effect.Effect> + readonly forkFetch: Effect.Effect> + readonly forkRefresh: Effect.Effect> + readonly fetchOnKeyChange: Effect.Effect readonly refreshOnWindowFocus: Effect.Effect } @@ -29,9 +29,9 @@ export const make = ( ): Effect.Effect, never, R> => Effect.gen(function*() { const context = yield* Effect.context() - const currentKeyRef = yield* SubscriptionRef.make(Option.none()) + const latestKeyRef = yield* SubscriptionRef.make(Option.none()) const stateRef = yield* SubscriptionRef.make(AsyncData.noData()) - const fiberRef = yield* SubscriptionRef.make(Option.none>()) + const fiberRef = yield* SubscriptionRef.make(Option.none>()) const interrupt = fiberRef.pipe( Effect.flatMap(Option.match({ @@ -57,12 +57,14 @@ export const make = ( const forkFetch = interrupt.pipe( Effect.andThen( Ref.set(stateRef, AsyncData.loading()).pipe( - Effect.andThen(queryRef), + Effect.andThen(latestKeyRef), Effect.flatMap(identity), - Effect.matchCauseEffect({ - onSuccess: v => Ref.set(stateRef, AsyncData.success(v)), - onFailure: c => Ref.set(stateRef, AsyncData.failure(c)), - }), + Effect.flatMap(key => query(key).pipe( + Effect.matchCauseEffect({ + onSuccess: v => Ref.set(stateRef, AsyncData.success(v)), + onFailure: c => Ref.set(stateRef, AsyncData.failure(c)), + }) + )), Effect.provide(context), Effect.fork, @@ -88,12 +90,14 @@ export const make = ( return AsyncData.refreshing(previous.previous) return AsyncData.loading() }).pipe( - - Effect.andThen(props.query()), - Effect.matchCauseEffect({ - onSuccess: v => Ref.set(stateRef, AsyncData.success(v)), - onFailure: c => Ref.set(stateRef, AsyncData.failure(c)), - }), + Effect.andThen(latestKeyRef), + Effect.flatMap(identity), + Effect.flatMap(key => query(key).pipe( + Effect.matchCauseEffect({ + onSuccess: v => Ref.set(stateRef, AsyncData.success(v)), + onFailure: c => Ref.set(stateRef, AsyncData.failure(c)), + }) + )), Effect.provide(context), Effect.fork, @@ -110,14 +114,22 @@ export const make = ( Effect.forkDaemon, ) + const fetchOnKeyChange = Effect.addFinalizer(() => interrupt).pipe( + Effect.andThen(Stream.runForEach(key, latestKey => + Ref.set(latestKeyRef, Option.some(latestKey)).pipe( + Effect.andThen(forkFetch) + ) + )) + ) + const refreshOnWindowFocus = Stream.runForEach( BrowserStream.fromEventListenerWindow("focus"), () => forkRefresh, ) return { - key: props.key, - query: props.query, + key, + query, stateRef, fiberRef, @@ -126,6 +138,7 @@ export const make = ( forkFetch, forkRefresh, + fetchOnKeyChange, refreshOnWindowFocus, } }) diff --git a/packages/extension-query/src/QueryService.ts b/packages/extension-query/src/QueryService.ts index a9a1e24..e872672 100644 --- a/packages/extension-query/src/QueryService.ts +++ b/packages/extension-query/src/QueryService.ts @@ -1,10 +1,10 @@ import * as AsyncData from "@typed/async-data" -import { Effect, Fiber, SubscriptionRef } from "effect" +import { type Cause, Effect, Fiber, SubscriptionRef } from "effect" export interface QueryService { readonly state: SubscriptionRef.SubscriptionRef> - readonly refresh: Effect.Effect> + readonly refresh: Effect.Effect> } export const Tag = (id: Id) => <