Marios G
Marios G

Reputation: 343

React Native: onBlur fires after onPress

I have a TouchableOpacity and a TextInput inside a ScrollView, when the TextInput is focused an I press the TouchableOpacity I wanna fire first the onBlur and then the onPress. What happens? with keyboardShouldPersistTaps="never" the onPress won't execute at all and with "handled" the onBlur won't execute at all and the input won't lose focus. I noticed that with TouchableOpacity imported from 'react-native-gesture-handler' and not from 'react-native', and keyboardShouldPersistTaps to "never" both onPress and onBlur will execute but the problem here is that the onBlur comes after the onPress! Any solution?

<ScrollView keyboardShouldPersistTaps="never">
  <TouchableOpacity onPress={() => console.log('press')}>
    <Text>Click me</Text>
  </TouchableOpacity>
  <TextInput value={value} onBlur={() => console.log('blur')} />
</ScrollView>

expected output with field focused and pressing the touchable:

blur
press

edit: a more complicated scenario

//one component
<ScrollView keyboardShouldPersistTaps="handled">
  <TextInput value={value} onBlur={() => console.log('blur')} /> 
  <TextInput value={value} onBlur={() => console.log('blur')} /> 
  <TextInput value={value} onBlur={() => console.log('blur')} /> 
</ScrollView>

//another component
<TouchableOpacity onPress={() => console.log('press')}>
    <Text>Click me</Text>
</TouchableOpacity>

Both Components would be rendered inside a Bigger Component.

Upvotes: 5

Views: 2389

Answers (1)

wjatek
wjatek

Reputation: 1006

You can use keyboardShouldPersistTaps="handled" and manually fire blur in onPress handler. It is not perfect solution, but it may be enough.

import * as React from 'react';
import {
  Text,
  View,
  StyleSheet,
  ScrollView,
  TouchableOpacity,
  TextInput,
} from 'react-native';
import Constants from 'expo-constants';

export default function App() {
  const [value, setValue] = React.useState();
  const input = React.useRef();

  return (
    <View style={styles.container}>
      <ScrollView keyboardShouldPersistTaps="handled">
        <TouchableOpacity
          onPress={() => {
            input.current.blur();
            console.log('press');
          }}>
          <Text>Click me</Text>
        </TouchableOpacity>
        <TextInput
          ref={input}
          value={value}
          onBlur={() => console.log('blur')}
        />
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
});

Edit: more complicated scenario - you can use Keyboard module to dismiss keyboard, which will automatically fire blur event on your inputs.

import * as React from 'react';
import {
  Text,
  View,
  StyleSheet,
  ScrollView,
  TouchableOpacity,
  TextInput,
  Keyboard,
} from 'react-native';
import Constants from 'expo-constants';

export default function App() {
  const [value, setValue] = React.useState();

  return (
    <View style={styles.container}>
      <ScrollView keyboardShouldPersistTaps="handled">
        <TextInput value={value} onBlur={() => console.log('blur')} />
        <TextInput value={value} onBlur={() => console.log('blur')} />
        <TextInput value={value} onBlur={() => console.log('blur')} />
      </ScrollView>
      <TouchableOpacity
        onPress={() => {
          Keyboard.dismiss();
          console.log('press');
        }}>
        <Text>Click me</Text>
      </TouchableOpacity>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
});

Upvotes: 4

Related Questions