diff --git a/bun.lockb b/bun.lockb index ea85420..2cfc905 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/reffuse/package.json b/packages/reffuse/package.json index e07c985..e0eb37d 100644 --- a/packages/reffuse/package.json +++ b/packages/reffuse/package.json @@ -29,6 +29,7 @@ "clean:node": "rm -rf node_modules" }, "devDependencies": { + "@typed/lazy-ref": "^0.3.3", "@types/react": "^19.0.4", "effect": "^3.12.1", "react": "^19.0.0" diff --git a/packages/reffuse/src/Reffuse.ts b/packages/reffuse/src/Reffuse.ts index 011c9aa..389601f 100644 --- a/packages/reffuse/src/Reffuse.ts +++ b/packages/reffuse/src/Reffuse.ts @@ -1,3 +1,4 @@ +import * as LazyRef from "@typed/lazy-ref" import { Context, Effect, ExecutionStrategy, Exit, Fiber, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect" import React from "react" import * as ReffuseContext from "./ReffuseContext.js" @@ -316,7 +317,7 @@ export class Reffuse { /** * Binds the state of a `SubscriptionRef` to the state of the React component. * - * Returns a [value, setter] tuple just like `React.useState` and triggers a re-render everytime the value of the ref changes. + * Returns a [value, setter] tuple just like `React.useState` and triggers a re-render everytime the value held by the ref changes. * * Note that the rules of React's immutable state still apply: updating a ref with the same value will not trigger a re-render. */ @@ -339,6 +340,32 @@ export class Reffuse { return [reactStateValue, setValue] } + /** + * Binds the state of a `LazyRef` from the `@typed/lazy-ref` package to the state of the React component. + * + * Returns a [value, setter] tuple just like `React.useState` and triggers a re-render everytime the value held by the ref changes. + * + * Note that the rules of React's immutable state still apply: updating a ref with the same value will not trigger a re-render. + */ + useLazyRefState(ref: LazyRef.LazyRef): [A, React.Dispatch>] { + const runSync = this.useRunSync() + + const initialState = React.useMemo(() => runSync(ref), []) + const [reactStateValue, setReactStateValue] = React.useState(initialState) + + 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] + } + }