diff --git a/bun.lock b/bun.lock index 14fcfe5..9ead317 100644 --- a/bun.lock +++ b/bun.lock @@ -58,9 +58,23 @@ "reffuse": "^0.1.1", }, }, + "packages/extension-query": { + "name": "@reffuse/extension-query", + "version": "0.1.0", + "devDependencies": { + "reffuse": "workspace:*", + }, + "peerDependencies": { + "@typed/async-data": "^0.13.1", + "@types/react": "^19.0.0", + "effect": "^3.13.0", + "react": "^19.0.0", + "reffuse": "^0.1.2", + }, + }, "packages/reffuse": { "name": "reffuse", - "version": "0.1.1", + "version": "0.1.2", "peerDependencies": { "@types/react": "^19.0.0", "effect": "^3.13.0", @@ -333,6 +347,8 @@ "@reffuse/extension-lazyref": ["@reffuse/extension-lazyref@workspace:packages/extension-lazyref"], + "@reffuse/extension-query": ["@reffuse/extension-query@workspace:packages/extension-query"], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.34.8", "", { "os": "android", "cpu": "arm" }, "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.34.8", "", { "os": "android", "cpu": "arm64" }, "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q=="], @@ -395,6 +411,8 @@ "@thilawyn/thilaschema": ["@thilawyn/thilaschema@0.1.4", "https://git.valverde.cloud/api/packages/Thilawyn/npm/%40thilawyn%2Fthilaschema/-/0.1.4/thilaschema-0.1.4.tgz", { "dependencies": { "remeda": "^2.17.0", "type-fest": "^4.26.1" } }, "sha512-o+lFjnRrD8N7kJtToKl+OYvVnOwaCGr1X9yMSX/8Y1n4KopOOGFSA9xqmx+MpMe3okp2Hq3Xu1aGHzFsZWxc2A=="], + "@typed/async-data": ["@typed/async-data@0.13.1", "", { "dependencies": { "@typed/lazy-ref": "^0.3.2", "effect": "^3.11.9" } }, "sha512-rKv3HQtoHeGJwZpEaTL0FAEKfqHcMr/x3GtgkE01p2tJiKjq1eVaPZYpweZEEF/zUutox7DQ14oH85x+ZpPA/Q=="], + "@typed/id": ["@typed/id@0.17.1", "", { "dependencies": { "effect": "^3.11.9" } }, "sha512-+nypUUw6PJWePD1aF1CHY4995hDF3VA9c8EBtp1M+pTnyLBZQIkgKbOKamimnl4U+ZV5I3qC+3q1Y4hpmxT+zw=="], "@typed/lazy-ref": ["@typed/lazy-ref@0.3.3", "", { "dependencies": { "effect": "^3.11.9" } }, "sha512-qJoy01/RFYwWBaWhQBzL3Ow20Q+CPybJ/KJnGNKzyDpRUFcEvd3YSQMqZjRdBZmG2wnEpjedAnlCx9ApvKJIlA=="], diff --git a/packages/extension-query/package.json b/packages/extension-query/package.json index 10b1b4f..d50650c 100644 --- a/packages/extension-query/package.json +++ b/packages/extension-query/package.json @@ -33,6 +33,7 @@ "reffuse": "workspace:*" }, "peerDependencies": { + "@typed/async-data": "^0.13.1", "@types/react": "^19.0.0", "effect": "^3.13.0", "react": "^19.0.0", diff --git a/packages/extension-query/src/index.ts b/packages/extension-query/src/index.ts index e5fbb02..ebd3518 100644 --- a/packages/extension-query/src/index.ts +++ b/packages/extension-query/src/index.ts @@ -1,29 +1,35 @@ -import * as LazyRef from "@typed/lazy-ref" -import { Effect, Stream } from "effect" +import * as AsyncData from "@typed/async-data" +import { Effect } from "effect" import * as React from "react" -import { ReffuseExtension, type ReffuseHelpers, SetStateAction } from "reffuse" +import { useState } from "react" +import { ReffuseExtension, type ReffuseHelpers } from "reffuse" -export const LazyRefExtension = ReffuseExtension.make(() => ({ - useLazyRefState( +export interface UseQueryProps { + effect(): Effect.Effect + readonly deps?: React.DependencyList +} + +export interface UseQueryResult { + readonly state: AsyncData.AsyncData +} + + +export const QueryExtension = ReffuseExtension.make(() => ({ + useQuery( this: ReffuseHelpers.ReffuseHelpers, - ref: LazyRef.LazyRef, - ): [A, React.Dispatch>] { - const runSync = this.useRunSync() + props: UseQueryProps, + ): UseQueryResult { + const [state, setState] = useState(AsyncData.noData()) - const initialState = React.useMemo(() => runSync(ref), []) - const [reactStateValue, setReactStateValue] = React.useState(initialState) + this.useFork(() => Effect.sync(() => setState(AsyncData.loading())).pipe( + Effect.andThen(props.effect()), + Effect.matchCause({ + onSuccess: v => setState(AsyncData.success(v)), + onFailure: c => setState(AsyncData.failure(c)), + }), + ), props.deps) - this.useFork(() => Stream.runForEach(ref.changes, v => Effect.sync(() => - setReactStateValue(v) - )), [ref]) - - const setValue = React.useCallback((setStateAction: React.SetStateAction) => - runSync(LazyRef.update(ref, prevState => - SetStateAction.value(setStateAction, prevState) - )), - [ref]) - - return [reactStateValue, setValue] - }, + return { state } + } }))