Skip to content

feat: focusedValue, and worklet focus handlers that see autoFocus - #57

Merged
ronickg merged 2 commits into
mainfrom
feat/focus-change-worklet
Sep 29, 2026
Merged

ronickg merged 2 commits into
mainfrom
feat/focus-change-worklet

Conversation

@ronickg

@ronickg ronickg commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

Why

Uno wants every input to show a blue border the moment it's focused, around the whole field row: a leading icon or country picker, the field itself and a trailing clear button. NitroInput's own frame (variant) only wraps the NitroInput view, and native views can't hold children. The other option, JS onFocus plus setState, costs a JS round trip before the border shows.

A glitch sweep in Uno also turned up an iOS keyboard bug, fixed in the same release (see the last item under What).

What

  • focusedValue prop: pass a shared value (useSharedValue(false)). The field sets it on the UI thread the moment focus changes, and onFocus / onBlur keep running on JS. The app then draws the border on its own row from the shared value, in the same frame the caret appears.
  • Focus worklets now register while rendering: previously they registered in an effect, so an autoFocus field's first focus could arrive before registration and be dropped. This also fixes worklet onFocus and useNitroInputState().focused for autoFocus fields.
  • iOS: no keyboard left over a screen popped back to. Going back from a screen whose focused field has keyboardHandoffMs, to a screen where no field takes the keyboard (Uno's account list after the amount screen), the keyboard stayed up over that screen for the whole hold.
    • Cause: React unmounts the popped screen's field before react-native-screens starts the pop, so at the handoff a pop looks exactly like a field swapped in place.
    • Fix: a hold now watches the leaving field's screen. When that screen starts to leave (react-native-screens takes it out of its navigation controller), the hold ends, unless a field is waiting for the keyboard (a covered field taking it back, or a focus() held for its view).
    • Result in Uno: the keyboard now slides out with the popped screen. Pops back to a field that had the keyboard still keep it.

Tests

  • bun run test: 171/171. The README and docs props tables are updated.
  • bun run typecheck
  • bun run test:harness:ios: 120/120. Two new keyboard tests: the keyboard goes after a pop to a screen whose field does not take it (it failed before the fix, with a 3 s hold), and it stays for a covered field that takes it back. The Harness can't load worklets, so focusedValue itself was checked in Uno (below).
  • In Uno, on the iPhone 17 simulator:
    • the autoFocus field shows the border on first focus;
    • moving focus between the three name fields switches the border in the same frame as the caret;
    • the phone field's border wraps the flag and country code;
    • recordings of amount → back (the keyboard leaves with the screen) and phone → back to the text step (the keyboard stays and changes type in place).
  • Android: no native change.

Release as 0.3.5 (the changelog heading is already ## 0.3.5).

🤖 Generated with Claude Code

Ronald Goedeke and others added 2 commits September 28, 2026 18:23
- `focusedValue`: a shared value the field keeps equal to its focus on the
  UI thread, alongside JS onFocus/onBlur. Lets an app draw a focus border
  around a row with icons or buttons beside the field in the frame focus
  lands, with no JS round trip.
- Focus worklets are registered while rendering instead of in an effect, so
  an autoFocus field's first focus (taken as its view attaches) reaches them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pped

React unmounts a popped screen's focused field before react-native-screens
starts the pop, so its keyboardHandoffMs hold could not tell a pop from a field
swapped in place and kept the keyboard up over the screen popped back to for
the whole hold. A hold now watches the leaving field's screen and ends as soon
as that screen starts to leave, unless a field is waiting to take the keyboard
(a covered field taking it back, a focus() held for its view).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ronickg
ronickg merged commit 17a8fc2 into main Sep 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant