react-native icon indicating copy to clipboard operation
react-native copied to clipboard

Add support for blur and focus on View

Open Abbondanzo opened this issue 6 months ago • 5 comments

Summary: As the title suggests: adds support strictly to View components on Android for onFocus and onBlur events. This is especially helpful for apps that respond to controller or remote inputs and aligns with existing support for the focusable prop.

In order to make this change cross-compatible with text inputs, TextInputFocusEvent has been deprecated in favor of the BlurEvent/FocusEvent types now available from core. Their type signatures are identical but BlurEvent/FocusEvent should be the type going forward for all views that intend to support focus/blur. Text inputs intentionally do not forward information about their state upon focus/blur and docs specifically call out onEndEditing as a means of reading state synchronously when blurring. Therefore, the changes to the native side to remove the event type specifically for text inputs is not breaking.

Changelog: [Android][Added] - Support for onFocus and onBlur function calls in View components

Differential Revision: D75238291

Abbondanzo avatar May 23 '25 18:05 Abbondanzo

This pull request was exported from Phabricator. Differential Revision: D75238291

facebook-github-bot avatar May 23 '25 18:05 facebook-github-bot

This pull request was exported from Phabricator. Differential Revision: D75238291

facebook-github-bot avatar May 23 '25 19:05 facebook-github-bot

cc @douglowder since there's some overlap with TV. However, this change only adds support for Android at the moment, and only targets the View component rather than others like Image. I'm keeping this change as minimal as possible, not baking it into the BaseViewManager because not all components emit focus and blur events.

I also haven't fully settled onto an appropriate solution for iOS, but I'd like there to be true support for focusable/keyboard navigation on there first.

Abbondanzo avatar May 23 '25 19:05 Abbondanzo

This pull request was exported from Phabricator. Differential Revision: D75238291

facebook-github-bot avatar May 27 '25 14:05 facebook-github-bot

This pull request was exported from Phabricator. Differential Revision: D75238291

facebook-github-bot avatar May 29 '25 01:05 facebook-github-bot

This pull request has been merged in facebook/react-native@af0a76cf5fdb8107294dff2c9aa0dbc36c7d5443.

facebook-github-bot avatar May 29 '25 04:05 facebook-github-bot

This pull request was successfully merged by @Abbondanzo in af0a76cf5fdb8107294dff2c9aa0dbc36c7d5443

When will my fix make it into a release? | How to file a pick request?

react-native-bot avatar May 29 '25 04:05 react-native-bot

Hi @Abbondanzo , I found this PR after facing some type issues with FocusEvent and BlurEvent by updating Expo SDK from 53 to 54.

Before the update, both onFocus and onBlur emitted NativeSyntheticEvent<TextInputFocusEventData>. Now they emit FocusEvent and BlurEvent. And they look like this: https://github.com/facebook/react-native/blob/36e1c0afed7a3e845d424e6ab89aa8ecbaf74bc6/packages/react-native/Libraries/Types/CoreEventTypes.d.ts#L247-L253

While before, TextInputFocusEventData contained little more info than TargetedEvent: https://github.com/facebook/react-native/blob/36e1c0afed7a3e845d424e6ab89aa8ecbaf74bc6/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts#L462-L465

And I printed event.nativeEvent in the console, it still contains eventCount and text: Screenshot 2025-10-04 at 9 27 36 PM

I'm accessing text of TextInput on both focus & blur. And I wonder if you removed it for any specific purpose? If not, then I believe it should be updated to:

export interface TargetedEvent { 
   target: number; 
 } 
  
export interface TextInputEvent extends TargetedEvent {
   text: string;
   eventCount: number;
}
 export type BlurEvent = NativeSyntheticEvent<TextInputEvent>; 
  
 export type FocusEvent = NativeSyntheticEvent<TextInputEvent>; 

Let me know what you think!

thisisgit avatar Oct 04 '25 12:10 thisisgit

Hi @thisisgit, would love to learn more about your use cases. The BlurEvent and FocusEvent types were intentionally made more abstract because all views now support focus and blurring events, not just text inputs. Since there is an existing event onEndEditing that emits the text input's state identically to that of the old blur event, that event is the recommendation if you are attempting to access text state. As the documentation for onBlur states:

If you are attempting to access the text value from nativeEvent keep in mind that the resulting value you get can be undefined which can cause unintended errors. If you are trying to find the last value of TextInput, you can use the onEndEditing event, which is fired upon completion of editing.

We haven't fully removed the text value from event payloads on iOS yet but it shouldn't be relied upon. Android does not emit this value from either onFocus or onBlur.

Abbondanzo avatar Oct 04 '25 19:10 Abbondanzo

@Abbondanzo Thanks for the quick reply. I didn't know text value isn't present on Android! I'm currently only working on iOS and wasn't aware of that.

So I was referring to that text field since I needed to store the text to wipTextRef and refer to it when focused TextInput unmounts:

useEffect(() => {
    return () => {
      if (!isFocusedRef.current) return;

      if (wipTextRef.current && wipTextRef.current.length > 0) {
        // Update
      } else {
        // Delete
      }
    };
  }, []);

And setting wipTextRef happens onFocus & onChangeText. onFocus because it needs to be initialized to handle case where user focuses the input but transition the screen without changing the text. And I was assigning the variable with the payload's text.

But if text value of the payload isn't reliable, then I guess I should use the variable that is being passed to defaultValue prop of the TextInput in onFocus event. I guess it's safe to use that variable for my case as I sync it with the text in TextInput every time it goes on blur. It could go wrong if the sync on blur don't work for some reason, but I think it's the only possible way without using value prop TextInput to be able to access up-to-date text on focus event. AFAIK there's no imperative way to retrieve text of TextInput as well so I guess if the current approach doesn't work, then I would have no choice but to make it controlled TextInput.

And thanks for sharing alternative of onBlur. It works perfectly

thisisgit avatar Oct 05 '25 07:10 thisisgit