0.1.3 #4
@@ -3,12 +3,13 @@ export * from "./useCallbackPromise.js"
|
|||||||
export * from "./useCallbackSync.js"
|
export * from "./useCallbackSync.js"
|
||||||
export * from "./useContext.js"
|
export * from "./useContext.js"
|
||||||
export * from "./useEffect.js"
|
export * from "./useEffect.js"
|
||||||
|
export * from "./useFork.js"
|
||||||
export * from "./useInput.js"
|
export * from "./useInput.js"
|
||||||
export * from "./useLayoutEffect.js"
|
export * from "./useLayoutEffect.js"
|
||||||
export * from "./useMemo.js"
|
export * from "./useMemo.js"
|
||||||
export * from "./useOnce.js"
|
export * from "./useOnce.js"
|
||||||
export * from "./useOptionalInput.js"
|
export * from "./useOptionalInput.js"
|
||||||
export * from "./useRefFromReactiveValue.js"
|
export * from "./useRefFromState.js"
|
||||||
export * from "./useRefState.js"
|
export * from "./useRefState.js"
|
||||||
export * from "./useScope.js"
|
export * from "./useScope.js"
|
||||||
export * from "./useStreamFromReactiveValues.js"
|
export * from "./useStreamFromReactiveValues.js"
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { type Duration, Effect, flow, identity, Option, type ParseResult, Ref, Schema, Stream, SubscriptionRef } from "effect"
|
import { type Duration, Effect, flow, identity, Option, type ParseResult, Ref, Schema, Stream, SubscriptionRef } from "effect"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { useCallbackSync } from "./useCallbackSync.js"
|
|
||||||
import { useFork } from "./useFork.js"
|
import { useFork } from "./useFork.js"
|
||||||
import { useOnce } from "./useOnce.js"
|
import { useOnce } from "./useOnce.js"
|
||||||
import { useSubscribeRefs } from "./useSubscribeRefs.js"
|
import { useRefFromState } from "./useRefFromState.js"
|
||||||
|
|
||||||
|
|
||||||
export namespace useInput {
|
export namespace useInput {
|
||||||
@@ -23,12 +22,14 @@ export namespace useInput {
|
|||||||
export const useInput: {
|
export const useInput: {
|
||||||
<A, R>(options: useInput.Options<A, R>): Effect.Effect<useInput.Result, ParseResult.ParseError, R>
|
<A, R>(options: useInput.Options<A, R>): Effect.Effect<useInput.Result, ParseResult.ParseError, R>
|
||||||
} = Effect.fnUntraced(function* <A, R>(options: useInput.Options<A, R>) {
|
} = Effect.fnUntraced(function* <A, R>(options: useInput.Options<A, R>) {
|
||||||
const internalRef = yield* useOnce(() => options.ref.pipe(
|
const internalState = React.useState(yield* useOnce(() => Effect.andThen(
|
||||||
Effect.andThen(Schema.encode(options.schema)),
|
options.ref,
|
||||||
Effect.andThen(SubscriptionRef.make),
|
Schema.encode(options.schema),
|
||||||
))
|
)))
|
||||||
|
const [value, setValue] = internalState
|
||||||
const [error, setError] = React.useState(Option.none<ParseResult.ParseError>())
|
const [error, setError] = React.useState(Option.none<ParseResult.ParseError>())
|
||||||
|
|
||||||
|
const internalRef = yield* useRefFromState(internalState)
|
||||||
yield* useFork(() => Effect.all([
|
yield* useFork(() => Effect.all([
|
||||||
// Sync the upstream state with the internal state
|
// Sync the upstream state with the internal state
|
||||||
// Only mutate the internal state if the upstream encoded value is actually different. This avoids infinite re-render loops.
|
// Only mutate the internal state if the upstream encoded value is actually different. This avoids infinite re-render loops.
|
||||||
@@ -54,11 +55,6 @@ export const useInput: {
|
|||||||
),
|
),
|
||||||
], { concurrency: "unbounded" }), [options.ref, options.schema, options.debounce, internalRef])
|
], { concurrency: "unbounded" }), [options.ref, options.schema, options.debounce, internalRef])
|
||||||
|
|
||||||
const [value] = yield* useSubscribeRefs(internalRef)
|
const onChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value), [])
|
||||||
const onChange = yield* useCallbackSync((e: React.ChangeEvent<HTMLInputElement>) => Ref.set(
|
|
||||||
internalRef,
|
|
||||||
e.target.value,
|
|
||||||
), [internalRef])
|
|
||||||
|
|
||||||
return { value, onChange, error }
|
return { value, onChange, error }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import { Effect, Ref, SubscriptionRef } from "effect"
|
|
||||||
import { useEffect } from "./useEffect.js"
|
|
||||||
import { useOnce } from "./useOnce.js"
|
|
||||||
|
|
||||||
|
|
||||||
export const useRefFromReactiveValue: {
|
|
||||||
<A>(value: A): Effect.Effect<SubscriptionRef.SubscriptionRef<A>>
|
|
||||||
} = Effect.fnUntraced(function*(value) {
|
|
||||||
const ref = yield* useOnce(() => SubscriptionRef.make(value))
|
|
||||||
yield* useEffect(() => Ref.set(ref, value), [value])
|
|
||||||
return ref
|
|
||||||
})
|
|
||||||
20
packages/effect-fc/src/hooks/Hooks/useRefFromState.ts
Normal file
20
packages/effect-fc/src/hooks/Hooks/useRefFromState.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Effect, Equivalence, Ref, Stream, SubscriptionRef } from "effect"
|
||||||
|
import type * as React from "react"
|
||||||
|
import { useEffect } from "./useEffect.js"
|
||||||
|
import { useFork } from "./useFork.js"
|
||||||
|
import { useOnce } from "./useOnce.js"
|
||||||
|
|
||||||
|
|
||||||
|
export const useRefFromState: {
|
||||||
|
<A>(state: readonly [A, React.Dispatch<React.SetStateAction<A>>]): Effect.Effect<SubscriptionRef.SubscriptionRef<A>>
|
||||||
|
} = Effect.fnUntraced(function*([value, setValue]) {
|
||||||
|
const ref = yield* useOnce(() => SubscriptionRef.make(value))
|
||||||
|
|
||||||
|
yield* useEffect(() => Ref.set(ref, value), [value])
|
||||||
|
yield* useFork(() => Stream.runForEach(
|
||||||
|
Stream.changesWith(ref.changes, Equivalence.strict()),
|
||||||
|
v => Effect.sync(() => setValue(v)),
|
||||||
|
), [setValue])
|
||||||
|
|
||||||
|
return ref
|
||||||
|
})
|
||||||
@@ -1,27 +1,32 @@
|
|||||||
import { TextFieldInput } from "@/lib/TextFieldInput"
|
import { TextFieldInput } from "@/lib/TextFieldInput"
|
||||||
import { runtime } from "@/runtime"
|
import { runtime } from "@/runtime"
|
||||||
import { Container } from "@radix-ui/themes"
|
import { Container, TextField } from "@radix-ui/themes"
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
import { createFileRoute } from "@tanstack/react-router"
|
||||||
import { Console, Schema, Stream, SubscriptionRef } from "effect"
|
import { Console, Schema, Stream, SubscriptionRef } from "effect"
|
||||||
import { Component, Memo } from "effect-fc"
|
import { Component, Memo } from "effect-fc"
|
||||||
import { useOnce } from "effect-fc/hooks"
|
import { useFork, useInput, useOnce, useRefState } from "effect-fc/hooks"
|
||||||
import { useFork } from "effect-fc/hooks/Hooks/useFork"
|
|
||||||
|
|
||||||
|
|
||||||
const IntTextFieldInput = TextFieldInput({ schema: Schema.NumberFromString.pipe(Schema.int()) })
|
const IntFromString = Schema.NumberFromString.pipe(Schema.int())
|
||||||
|
|
||||||
|
const IntTextFieldInput = TextFieldInput({ schema: IntFromString })
|
||||||
const StringTextFieldInput = TextFieldInput({ schema: Schema.String })
|
const StringTextFieldInput = TextFieldInput({ schema: Schema.String })
|
||||||
|
|
||||||
const Input = Component.makeUntraced(function* Input() {
|
const Input = Component.makeUntraced(function* Input() {
|
||||||
const IntTextFieldInputFC = yield* IntTextFieldInput
|
const IntTextFieldInputFC = yield* IntTextFieldInput
|
||||||
const StringTextFieldInputFC = yield* StringTextFieldInput
|
const StringTextFieldInputFC = yield* StringTextFieldInput
|
||||||
|
|
||||||
const intRef = yield* useOnce(() => SubscriptionRef.make(0))
|
const intRef1 = yield* useOnce(() => SubscriptionRef.make(0))
|
||||||
|
const intRef2 = yield* useOnce(() => SubscriptionRef.make(0))
|
||||||
const stringRef = yield* useOnce(() => SubscriptionRef.make(""))
|
const stringRef = yield* useOnce(() => SubscriptionRef.make(""))
|
||||||
yield* useFork(() => Stream.runForEach(intRef.changes, Console.log), [intRef])
|
// yield* useFork(() => Stream.runForEach(intRef1.changes, Console.log), [intRef1])
|
||||||
|
|
||||||
|
const input2 = yield* useInput({ schema: IntFromString, ref: intRef2 })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<IntTextFieldInputFC ref={intRef} />
|
<IntTextFieldInputFC ref={intRef1} />
|
||||||
|
<TextField.Root value={input2.value} onChange={input2.onChange} />
|
||||||
<StringTextFieldInputFC ref={stringRef} />
|
<StringTextFieldInputFC ref={stringRef} />
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user