ifelsething logoifelsething

Keyboard Key Press Events of React Native TextInput

Published On: 2024-04-11
Posted By: Harish

Keyboard Key Press Events of React Native TextInput

Like press events of text input, we can also get the key press events of text input using onKeyPress callback.

This callback returns the event with the key details. Please note that this callback only works for soft keyboard and not for hardware keyboard.

Let's see this callback in action.

Create A New Project

Create a new react-native project by using npx. Check documentation for creating a new react native project.

npx react-native@latest init TextInputRN

Example Implementation

We will create a simple text input for a name.

Import the TextInput component in the App.tsx file and add basic required props for name.

Now add onKeyPress callback to the name input.

//App.tsx
import { TextInput } from 'react-native';
...
<TextInput
  style={styles.input}
  keyboardType='default'
  autoCorrect={false}
  autoCapitalize="none"
  placeholder='Enter Name'
  placeholderTextColor='gray'
  onKeyPress={event => {
    console.log(event.nativeEvent);
  }}
/>
...

Run the app,

#for Android
npx react-native run-android

#for ios
npx react-native run-ios

After rendering, enter text inside text input and check logs. You will see output similar to below log for android devices.

LOG  {"key": "i"}
LOG  {"key": "f"}
LOG  {"key": "e"}
LOG  {"key": "l"}
LOG  {"key": "s"}
LOG  {"key": "e"}
LOG  {"key": "t"}
LOG  {"key": "h"}
LOG  {"key": "i"}
LOG  {"key": "n"}
LOG  {"key": "g"}

From the above log for android, we can see that the pressed key is showing up in the log. So, every time a key is pressed, onKeyPress callback returns an event with pressed key details.

Keyboard Key Press Events of React Native TextInput

For iOS, output is similar to android but has extra properties.

LOG  {"eventCount": 0, "key": "i", "target": 389}
LOG  {"eventCount": 1, "key": "f", "target": 389}
LOG  {"eventCount": 2, "key": "e", "target": 389}
LOG  {"eventCount": 3, "key": "l", "target": 389}
LOG  {"eventCount": 4, "key": "s", "target": 389}
LOG  {"eventCount": 5, "key": "e", "target": 389}
LOG  {"eventCount": 6, "key": "t", "target": 389}
LOG  {"eventCount": 7, "key": "h", "target": 389}
LOG  {"eventCount": 8, "key": "i", "target": 389}
LOG  {"eventCount": 9, "key": "n", "target": 389}
LOG  {"eventCount": 10, "key": "g", "target": 389}

Sometimes, two events are fired on the text input key press in iOS devices. One correct key event followed by a backspace event is fired. I encountered this problem for iOS devices.

As a workaround, I've written a function with a global variable which checks the timestamp and assigns it to the global variable for the first event and will assign a 0 for the immediately followed backspace event.

Haven't tested for the production, but for the sake of this post, I'm using that workaround.

...
const onKeyPressForiOS = useCallback((event: NativeSyntheticEvent<TextInputKeyPressEventData>) => {
  const eventTimestamp = event.timeStamp
  const nativeEvent = event.nativeEvent;
  if (timestamp == 0) {
    if (nativeEvent.key === 'Backspace') {
      setKeyPress(nativeEvent);
      return;
    }
    timestamp = eventTimestamp;
    setKeyPress(nativeEvent);
  } else {
    timestamp = 0;
  }
}, []);
...
Keyboard Key Press Events of React Native TextInput

Complete code of our example,

//App.tsx
import React, { useCallback, useState } from "react";
import {
  Text,
  StyleSheet,
  TextInput,
  SafeAreaView,
  StatusBar,
  View,
  TextInputKeyPressEventData,
  NativeSyntheticEvent,
  Platform
} from "react-native";

export default function App() {
  let timestamp = 0;
  const [keyPress, setKeyPress] = useState<TextInputKeyPressEventData | undefined>();
  const onKeyPressForiOS = useCallback((event: NativeSyntheticEvent<TextInputKeyPressEventData>) => {
    const eventTimestamp = event.timeStamp
    const nativeEvent = event.nativeEvent;
    if (timestamp == 0) {
      if (nativeEvent.key === 'Backspace') {
        setKeyPress(nativeEvent);
        return;
      }
      timestamp = eventTimestamp;
      setKeyPress(nativeEvent);
    } else {
      timestamp = 0;
    }
  }, []);
  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: 'white' }}>
      <StatusBar
        barStyle="dark-content"
      />
      <View style={styles.container} >
        <Text style={styles.text}>
          ifelsething.com
        </Text>
        <Text style={styles.text}>
          text input keyboard key press events
        </Text>
        <TextInput
          style={styles.input}
          keyboardType='default'
          placeholder='Enter Name'
          placeholderTextColor='gray'
          autoCorrect={false}
          autoCapitalize="none"
          onKeyPress={event => {
            if(Platform.OS == 'ios') {
              onKeyPressForiOS(event);
            } else {
              console.log("Key Press: ", event.nativeEvent)
              setKeyPress(event.nativeEvent);
            }
          }}
        />
        <Text style={styles.text}>
          Output: {keyPress ? JSON.stringify(keyPress) : 'No Key Pressed'}
        </Text>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    margin: 10,
    gap: 20
  },
  input: {
    borderColor: 'blue',
    borderRadius: 10,
    borderWidth: 1,
    fontSize: 15,
    padding: 10,
    color: 'black',
  },
  text: {
    fontSize: 15,
    color: 'black',
    fontStyle: 'italic'
  },
});

Share is Caring

Related Posts