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

iOS: Multiline TextInput onKeyPress event handler unexpectedly receives a Backspace event

Open jbrowning opened this issue 2 years ago • 15 comments

Description

The TextInput onKeyPress event handler receives an unexpected Backspace event when multiline is true. The exact behavior varies between iOS 15 and iOS 16:

  • iOS 15: The Backspace event is received after value is reset to an empty string
  • iOS 16: The Backspace event is received on EVERY change AND when value is reset to an empty string

This has been tested on iOS 15.5, 16.4, and 16.5.

Possibly related: https://github.com/facebook/react-native/issues/34891

React Native Version

0.71.11

Output of npx react-native info

System: OS: macOS 13.4 CPU: (16) x64 Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz Memory: 27.38 GB / 64.00 GB Shell: 5.9 - /usr/local/bin/zsh Binaries: Node: 18.16.0 - ~/.asdf/installs/nodejs/18.16.0/bin/node Yarn: 1.22.19 - /usr/local/bin/yarn npm: 9.6.6 - ~/.asdf/plugins/nodejs/shims/npm Watchman: Not Found Managers: CocoaPods: 1.12.1 - /Users/jeff/.asdf/shims/pod SDKs: iOS SDK: Platforms: DriverKit 22.4, iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4 Android SDK: Not Found IDEs: Android Studio: 2022.2 AI-222.4459.24.2221.10121639 Xcode: 14.3.1/14E300c - /usr/bin/xcodebuild Languages: Java: 11.0.13 - /Users/jeff/.asdf/shims/javac npmPackages: @react-native-community/cli: Not Found react: 18.2.0 => 18.2.0 react-native: 0.71.11 => 0.71.11 react-native-macos: Not Found npmGlobalPackages: react-native: Not Found

Steps to reproduce

  1. Add a <TextInput multiline /> component and log all events received by onKeyPress
  2. Type into the input
  3. Observe that an event with nativeEvent.key set to 'Backspace' is received after every onKeyPress event is received for the typed key

Snack, code example, screenshot, or link to a repository

https://github.com/jbrowning/RN071TextInputBackspace

jbrowning avatar Jun 19 '23 22:06 jbrowning

Same issue here

rodrigoeidelvein avatar Jun 19 '23 22:06 rodrigoeidelvein

Cc: @NickGerleman

Pranav-yadav avatar Jun 21 '23 15:06 Pranav-yadav

I'm having the same issue on iOS 16.4 with a single line textfield.

<TextInput onKeyPress={(e) => console.log(e.nativeEvent} />

Pressing a random key, such as 'a' results in the following log:

{"eventCount": 1, "key": "a", "target": 1205}
{"eventCount": 2, "key": "Backspace", "target": 1205}

I'm guessing it's related to iOS 16.4, because some of my components that were working before are now broken

My current clunky workaround is to check for the distance between events, under 100 milliseconds I ignore the Backspace:

function onKeyPress(e) {
    if (e.nativeEvent.key === 'Backspace' && Date.now() - latestTimestamp > 100) {
        // Handle backspace actually wanted by the user
    }

    if (e.nativeEvent.key !== 'Backspace') {
      latestTimestamp = Date.now()
    }
}

gbalduzzi avatar Jun 28 '23 08:06 gbalduzzi

I'm also having this problem on a physical iPhone 11 Pro running iOS 16.1.1. My project is using Expo. The behaviour does not occur on Android.

chadders404 avatar Jun 28 '23 19:06 chadders404

I have the same issue, after each key press event of the TextInput component, I have a Backspace.

LOG F LOG Backspace

React Native: 0.71.6 IOS: 16.4

mokhtarHamdoune avatar Aug 19 '23 14:08 mokhtarHamdoune

We also seem to get duplicated Enter key presses.

System:
    OS: macOS 13.5.2
    CPU: (8) arm64 Apple M1 Pro
    Memory: 95.41 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 18.16.1 - ~/.nvm/versions/node/v18.16.1/bin/node
    Yarn: Not Found
    npm: 9.5.1 - ~/.nvm/versions/node/v18.16.1/bin/npm
    Watchman: 2023.07.03.00 - /opt/homebrew/bin/watchman
  Managers:
    CocoaPods: 1.12.1 - /Users/tibbe/.rbenv/shims/pod
  SDKs:
    iOS SDK:
      Platforms: DriverKit 23.0, iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0
    Android SDK: Not Found
  IDEs:
    Android Studio: 2022.2 AI-222.4459.24.2221.10121639
    Xcode: 15.0/15A240d - /usr/bin/xcodebuild
  Languages:
    Java: 11.0.19 - /usr/bin/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: 18.2.0 => 18.2.0 
    react-native: 0.71.13 => 0.71.13 
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

tibbe avatar Sep 19 '23 11:09 tibbe

Same issue here, but with multiline = false. And also I hit on return key in my iOS simulator, it does not show any event

pee0803 avatar Nov 23 '23 07:11 pee0803

same here in 0.72.7 version

vinaykumar0339 avatar Dec 04 '23 16:12 vinaykumar0339

I experience the same issue React Native: 0.70.10 iOS: 17.2, simulator

raynox avatar Jan 23 '24 15:01 raynox

I am still experiencing this issue.

iOS 17.4.1 , iPhone 11 React Native: 0.73.6 Expo Managed Workflow Expo SDK: 50.0.14

Has anyone got more information or found a way around this problem?

chadders404 avatar Apr 08 '24 00:04 chadders404

I am also experiencing this issue

tomruijgrok avatar Apr 10 '24 10:04 tomruijgrok

I am facing this issue also

asamiz avatar Apr 22 '24 10:04 asamiz

still running into this issue as of react native 0.74.2 on iOS

cchyung avatar Jul 19 '24 21:07 cchyung

Just a note on a workaround we've been using: we wrap our TextInput so that we ignore all backspaces within 50 ms. of another keypress:

import { NativeSyntheticEvent, TextInput, TextInputProps, TextInputKeyPressEventData } from 'react-native';

function OurTextInput(props: TextInputProps) {
  const { onKeyPress } = props;
  const latestNonBackspaceKeyPressMsRef = useRef<number | null>(null);

  const handleKeyPress = useCallback((e: NativeSyntheticEvent<TextInputKeyPressEventData>) => {
    if (!onKeyPress) {
      return;
    }

    // This is a workaround for a bug where the react native text input
    // registers a Backspace key press after every other keypress
    //
    // documented here:
    // https://github.com/facebook/react-native/issues/37967
    if (e.nativeEvent.key === 'Backspace') {
      if (
        !latestNonBackspaceKeyPressMsRef.current ||
        Date.now() - latestNonBackspaceKeyPressMsRef.current > 80
      ) {
        onKeyPress(e);
      }
    } else {
      latestNonBackspaceKeyPressMsRef.current = Date.now();
      onKeyPress(e);
    }
  }, [onKeyPress]);

  return <TextInput {...props} onKeyPress={handleKeyPress} />
}

pxpeterxu avatar Aug 16 '24 22:08 pxpeterxu