diff --git a/packages/extension-query/src/index.ts b/packages/extension-query/src/index.ts
index 58b8249..c528f62 100644
--- a/packages/extension-query/src/index.ts
+++ b/packages/extension-query/src/index.ts
@@ -1,17 +1,18 @@
import * as AsyncData from "@typed/async-data"
-import { Effect, Scope } from "effect"
+import { Effect, Fiber, Option, Ref } from "effect"
import * as React from "react"
import { useState } from "react"
import { ReffuseExtension, type ReffuseHelpers } from "reffuse"
export interface UseQueryProps {
- effect: () => Effect.Effect
- readonly deps?: React.DependencyList
+ effect: () => Effect.Effect
+ readonly deps: React.DependencyList
}
-export interface UseQueryResult {
+export interface UseQueryResult {
readonly state: AsyncData.AsyncData
+ readonly refresh: Effect.Effect
}
@@ -19,17 +20,44 @@ export const QueryExtension = ReffuseExtension.make(() => ({
useQuery(
this: ReffuseHelpers.ReffuseHelpers,
props: UseQueryProps,
- ): UseQueryResult {
+ ): UseQueryResult {
+ const fiberRef = this.useRef(Option.none>())
const [state, setState] = useState(AsyncData.noData())
- this.useFork(() => Effect.sync(() => setState(AsyncData.loading())).pipe(
- Effect.andThen(props.effect()),
+ const interruptRunningQuery = React.useMemo(() => fiberRef.pipe(
+ Effect.flatMap(Option.match({
+ onSome: Fiber.interrupt,
+ onNone: () => Effect.void,
+ }))
+ ), [])
+
+ const runQuery = React.useMemo(() => props.effect().pipe(
Effect.matchCause({
onSuccess: v => setState(AsyncData.success(v)),
onFailure: c => setState(AsyncData.failure(c)),
- }),
+ })
), props.deps)
- return { state }
+ const refresh = React.useMemo(() => interruptRunningQuery.pipe(
+ Effect.andThen(Effect.sync(() => setState(prev =>
+ AsyncData.isSuccess(prev) || AsyncData.isFailure(prev)
+ ? AsyncData.refreshing(prev)
+ : AsyncData.loading()
+ ))),
+ Effect.andThen(runQuery),
+ Effect.forkDaemon,
+
+ Effect.flatMap(fiber => Ref.set(fiberRef, Option.some(fiber))),
+ ), [runQuery])
+
+ this.useEffect(() => interruptRunningQuery.pipe(
+ Effect.andThen(Effect.sync(() => setState(AsyncData.loading()))),
+ Effect.andThen(runQuery),
+ Effect.forkDaemon,
+
+ Effect.flatMap(fiber => Ref.set(fiberRef, Option.some(fiber))),
+ ), [runQuery])
+
+ return { state, refresh }
}
}))