diff --git a/packages/example/package.json b/packages/example/package.json index 24c7735..e1a234e 100644 --- a/packages/example/package.json +++ b/packages/example/package.json @@ -32,6 +32,7 @@ "@effect/platform": "^0.77.2", "@effect/platform-browser": "^0.56.2", "@radix-ui/themes": "^3.2.0", + "@reffuse/extension-lazyref": "workspace:*", "@typed/id": "^0.17.1", "@typed/lazy-ref": "^0.3.3", "effect": "^3.13.2", diff --git a/packages/example/src/reffuse.ts b/packages/example/src/reffuse.ts index ac2e2b8..0aefd92 100644 --- a/packages/example/src/reffuse.ts +++ b/packages/example/src/reffuse.ts @@ -1,5 +1,6 @@ import { HttpClient } from "@effect/platform" import { Clipboard, Geolocation, Permissions } from "@effect/platform-browser" +import { LazyRefExtension } from "@reffuse/extension-lazyref" import { Reffuse, ReffuseContext } from "reffuse" @@ -11,7 +12,8 @@ export const GlobalContext = ReffuseContext.make< >() export class GlobalReffuse extends Reffuse.Reffuse.pipe( - Reffuse.withContexts(GlobalContext) + Reffuse.withExtension(LazyRefExtension), + Reffuse.withContexts(GlobalContext), ) {} export const R = new GlobalReffuse() diff --git a/packages/example/src/routeTree.gen.ts b/packages/example/src/routeTree.gen.ts index b1e3d9c..98b4224 100644 --- a/packages/example/src/routeTree.gen.ts +++ b/packages/example/src/routeTree.gen.ts @@ -14,6 +14,7 @@ import { Route as rootRoute } from './routes/__root' import { Route as TimeImport } from './routes/time' import { Route as TestsImport } from './routes/tests' import { Route as PromiseImport } from './routes/promise' +import { Route as LazyrefImport } from './routes/lazyref' import { Route as CountImport } from './routes/count' import { Route as BlankImport } from './routes/blank' import { Route as IndexImport } from './routes/index' @@ -38,6 +39,12 @@ const PromiseRoute = PromiseImport.update({ getParentRoute: () => rootRoute, } as any) +const LazyrefRoute = LazyrefImport.update({ + id: '/lazyref', + path: '/lazyref', + getParentRoute: () => rootRoute, +} as any) + const CountRoute = CountImport.update({ id: '/count', path: '/count', @@ -81,6 +88,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof CountImport parentRoute: typeof rootRoute } + '/lazyref': { + id: '/lazyref' + path: '/lazyref' + fullPath: '/lazyref' + preLoaderRoute: typeof LazyrefImport + parentRoute: typeof rootRoute + } '/promise': { id: '/promise' path: '/promise' @@ -111,6 +125,7 @@ export interface FileRoutesByFullPath { '/': typeof IndexRoute '/blank': typeof BlankRoute '/count': typeof CountRoute + '/lazyref': typeof LazyrefRoute '/promise': typeof PromiseRoute '/tests': typeof TestsRoute '/time': typeof TimeRoute @@ -120,6 +135,7 @@ export interface FileRoutesByTo { '/': typeof IndexRoute '/blank': typeof BlankRoute '/count': typeof CountRoute + '/lazyref': typeof LazyrefRoute '/promise': typeof PromiseRoute '/tests': typeof TestsRoute '/time': typeof TimeRoute @@ -130,6 +146,7 @@ export interface FileRoutesById { '/': typeof IndexRoute '/blank': typeof BlankRoute '/count': typeof CountRoute + '/lazyref': typeof LazyrefRoute '/promise': typeof PromiseRoute '/tests': typeof TestsRoute '/time': typeof TimeRoute @@ -137,10 +154,25 @@ export interface FileRoutesById { export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/blank' | '/count' | '/promise' | '/tests' | '/time' + fullPaths: + | '/' + | '/blank' + | '/count' + | '/lazyref' + | '/promise' + | '/tests' + | '/time' fileRoutesByTo: FileRoutesByTo - to: '/' | '/blank' | '/count' | '/promise' | '/tests' | '/time' - id: '__root__' | '/' | '/blank' | '/count' | '/promise' | '/tests' | '/time' + to: '/' | '/blank' | '/count' | '/lazyref' | '/promise' | '/tests' | '/time' + id: + | '__root__' + | '/' + | '/blank' + | '/count' + | '/lazyref' + | '/promise' + | '/tests' + | '/time' fileRoutesById: FileRoutesById } @@ -148,6 +180,7 @@ export interface RootRouteChildren { IndexRoute: typeof IndexRoute BlankRoute: typeof BlankRoute CountRoute: typeof CountRoute + LazyrefRoute: typeof LazyrefRoute PromiseRoute: typeof PromiseRoute TestsRoute: typeof TestsRoute TimeRoute: typeof TimeRoute @@ -157,6 +190,7 @@ const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, BlankRoute: BlankRoute, CountRoute: CountRoute, + LazyrefRoute: LazyrefRoute, PromiseRoute: PromiseRoute, TestsRoute: TestsRoute, TimeRoute: TimeRoute, @@ -175,6 +209,7 @@ export const routeTree = rootRoute "/", "/blank", "/count", + "/lazyref", "/promise", "/tests", "/time" @@ -189,6 +224,9 @@ export const routeTree = rootRoute "/count": { "filePath": "count.tsx" }, + "/lazyref": { + "filePath": "lazyref.tsx" + }, "/promise": { "filePath": "promise.tsx" }, diff --git a/packages/example/src/routes/lazyref.tsx b/packages/example/src/routes/lazyref.tsx new file mode 100644 index 0000000..586f25c --- /dev/null +++ b/packages/example/src/routes/lazyref.tsx @@ -0,0 +1,31 @@ +import { R } from "@/reffuse" +import { Button, Text } from "@radix-ui/themes" +import { createFileRoute } from "@tanstack/react-router" +import * as LazyRef from "@typed/lazy-ref" +import { Suspense, use } from "react" + + +export const Route = createFileRoute("/lazyref")({ + component: RouteComponent +}) + +function RouteComponent() { + const promise = R.usePromise(LazyRef.of(0)) + + return ( + Loading...}> + + + ) +} + +function LazyRefComponent({ promise }: { readonly promise: Promise> }) { + const ref = use(promise) + const [value, setValue] = R.useLazyRefState(ref) + + return ( + + ) +} diff --git a/packages/extension-lazyref/src/index.ts b/packages/extension-lazyref/src/index.ts index 301509a..42728c5 100644 --- a/packages/extension-lazyref/src/index.ts +++ b/packages/extension-lazyref/src/index.ts @@ -1,10 +1,10 @@ import * as LazyRef from "@typed/lazy-ref" import { Effect, Stream } from "effect" import * as React from "react" -import { ReffuseExtension, ReffuseHelpers, SetStateAction } from "reffuse" +import { ReffuseExtension, type ReffuseHelpers, SetStateAction } from "reffuse" -export const withLazyRef = ReffuseExtension.make(() => ({ +export const LazyRefExtension = ReffuseExtension.make(() => ({ useLazyRefState( this: ReffuseHelpers.ReffuseHelpers, ref: LazyRef.LazyRef, diff --git a/packages/reffuse/src/Reffuse.ts b/packages/reffuse/src/Reffuse.ts index 0721298..87d13e4 100644 --- a/packages/reffuse/src/Reffuse.ts +++ b/packages/reffuse/src/Reffuse.ts @@ -1,10 +1,12 @@ import type * as ReffuseContext from "./ReffuseContext.js" +import type * as ReffuseExtension from "./ReffuseExtension.js" import * as ReffuseHelpers from "./ReffuseHelpers.js" import type { Merge, StaticType } from "./types.js" export class Reffuse extends ReffuseHelpers.make() {} + export const withContexts = >( ...contexts: [...{ [K in keyof R2]: ReffuseContext.ReffuseContext }] ) => @@ -27,3 +29,19 @@ export const withContexts = >( ) => class extends self { static readonly contexts = [...self.contexts, ...contexts] } as any + + +export const withExtension = (extension: ReffuseExtension.ReffuseExtension) => + < + BaseClass extends ReffuseHelpers.ReffuseHelpersClass, + R + >( + self: BaseClass & ReffuseHelpers.ReffuseHelpersClass + ): ( + { new(): Merge, A> } & + StaticType + ) => { + const class_ = class extends self {} + Object.assign(class_.prototype, extension()) + return class_ as any + } diff --git a/packages/reffuse/src/ReffuseContext.tsx b/packages/reffuse/src/ReffuseContext.tsx index c914a61..e7e723a 100644 --- a/packages/reffuse/src/ReffuseContext.tsx +++ b/packages/reffuse/src/ReffuseContext.tsx @@ -3,7 +3,6 @@ import * as React from "react" import * as ReffuseRuntime from "./ReffuseRuntime.js" -// TODO: merge this with the Provider, just like React 19 contexts export class ReffuseContext { readonly Context = React.createContext>(null!) @@ -11,7 +10,7 @@ export class ReffuseContext { constructor() { // TODO: scope the layer creation - this.Provider = (props) => { + this.Provider = props => { const runtime = ReffuseRuntime.useRuntime() const value = React.useMemo(() => Effect.context().pipe( diff --git a/packages/reffuse/src/ReffuseExtension.ts b/packages/reffuse/src/ReffuseExtension.ts index ce1fdea..3dda6c2 100644 --- a/packages/reffuse/src/ReffuseExtension.ts +++ b/packages/reffuse/src/ReffuseExtension.ts @@ -1,18 +1,7 @@ -import type * as ReffuseHelpers from "./ReffuseHelpers.js" -import type { Merge, StaticType } from "./types.js" +export interface ReffuseExtension { + (): A + readonly Type: A +} - -export const make = (extension: () => Ext) => - < - BaseClass extends ReffuseHelpers.ReffuseHelpersClass, - R - >( - self: BaseClass & ReffuseHelpers.ReffuseHelpersClass - ): ( - { new(): Merge, Ext> } & - StaticType - ) => { - const class_ = class extends self {} - Object.assign(class_.prototype, extension()) - return class_ as any - } +export const make = (extension: () => A): ReffuseExtension => + extension as ReffuseExtension