Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Native - inline pressable component with hitSlop inline with text

A client wants a paragraph of text in a React Native app, where there is inline text that is pressable. The problem is that I can't put hitSlop on a Text and if I use TouchableOpacity, it won't display inline with the text - each is its own block.

Here is what I've tried:

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={ styles.text }>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi </Text>

        <TouchableOpacity
          onPress={ () => Alert.alert('howdy') }
          hitSlop={ { top: 15, right: 15, bottom: 15, left: 15 } }
        >
          <Text style={ styles.buttonText }>ut aliquip ex ea commodo consequat.</Text>
        </TouchableOpacity>

        <Text style={ styles.text }>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    marginTop: 50,
    flexDirection: 'row',
    flexWrap: 'wrap',
  },
  text: {
    flexWrap: 'wrap',
  },
  buttonText: {
    color: 'red',
  }
});

There is a snack here.

Is there a way to get some hitSlop on a Text? Or is there a way to get a TouchableOpacity inline with other text? In either case, the hitSlop would need to overlap the inert text that surrounds it.

Any ideas? Thanx.

Addendum: I forgot to mention that on Android, you can't have a View (which TouchableOpacity inherits) as a child of a Text. It works on iOS but not Android, which is unacceptable.

like image 228
Kevin Avatar asked Aug 08 '26 01:08

Kevin


1 Answers

Your original solution is very close. The reason it doesn't work is because flexWrap keeps entire elements (View or Text) together rather than wrapping at the nearest word.

You can get around this by splitting your long text string and creating a separate TouchableOpacity for each word. That way flexWrap will behave like a normal word wrap.

Here's the Snack

const touchableText = 'ut aliquip ex ea commodo consequat.';
const loremText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.';
const ipsumText = 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';

export default () => (
  <View style={styles.container}>
    <CompoundText text={loremText} />
    <CompoundTextLink text={touchableText} />
    <CompoundText text={ipsumText} />
  </View>
);

const CompoundText = ({text}) => 
  text.split(' ').map(word => <Text>{word} </Text>);

const CompoundTextLink = ({ text }) => 
  text.split(' ').map(word => <TextLink word={word} />);

const TextLink = ({ word }) => (
  <TouchableOpacity
    hitSlop={{top: 10, bottom: 10, left: 10, right: 10}}
    onPress={() => Alert.alert('howdy')}
    style={styles.zIndex1}
  >
    <Text style={styles.buttonText}>{word} </Text>
  </TouchableOpacity>
);

const styles = StyleSheet.create({
  container: {
    marginTop: 50,
    flexDirection: 'row',
    flexWrap: 'wrap',
  },
  buttonText: {
    color: 'red',
  }
});
like image 118
Tyler Avatar answered Aug 09 '26 14:08

Tyler



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!