diff --git a/packages/effect-fc/src/hooks/Hooks/useInput.ts b/packages/effect-fc/src/hooks/Hooks/useInput.ts index 7d6a0ff..8406267 100644 --- a/packages/effect-fc/src/hooks/Hooks/useInput.ts +++ b/packages/effect-fc/src/hooks/Hooks/useInput.ts @@ -30,14 +30,19 @@ export const useInput: { const [error, setError] = React.useState(Option.none()) yield* useFork(() => Effect.all([ + // 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. Stream.runForEach(options.ref.changes, upstreamValue => - Effect.andThen(internalRef, internalValue => - upstreamValue !== internalValue - ? Effect.andThen(Schema.encode(options.schema)(upstreamValue), v => Ref.set(internalRef, v)) - : Effect.void + Effect.andThen( + Effect.all([Schema.encode(options.schema)(upstreamValue), internalRef]), + ([encodedUpstreamValue, internalValue]) => Effect.when( + Ref.set(internalRef, encodedUpstreamValue), + () => encodedUpstreamValue !== internalValue, + ), ) ), + // Sync all changes to the internal state with upstream Stream.runForEach( internalRef.changes.pipe(options.debounce ? Stream.debounce(options.debounce) : identity), flow(