Home > Net >  How Can I wrap a nested Text in a Touchable/Pressable component?
How Can I wrap a nested Text in a Touchable/Pressable component?

Time:05-05

I am trying to wrap a nested text component in a TouchableOpacity or similar.

The use case for this is to parse links and other special pieces of text out of a longer message and make them interactable (links, hashtags, etc...)

ex. "Hello Testing [google.com] for [#energy] and [#candy] in the morning"

where [] represent interactable text

There are many questions like this on Stack Overflow, but in other cases, the OP was able to use a workaround that doesn't work in my case.

Here is an example:

<View style={someStyle}>
  <Text style={sharedTextStyles}>
    {textChunks.map((chunk:string)=>{
      const text = (
        <Text style={innerTextStyle}>
          {chunk}
        </Text>
      )
      return matchesSomeRegex() ?
        <TouchableOpacity
          onPressIn={someHandler0}
          onPress={someHandler1}
          onLongPress={someHandler2}
        >
          {text}
        </TouchableOpacity> :
        text
    })}
  </Text>
</View>

The above code works, but the pressable chunks are seemingly impossible to position within the text lines (too high, and impossible to reposition with styling)

I have tried splitting the string on " " and using flex positioning, which fixes the alignment, but creates new issues with text wrapping, spaces, and especially new lines that I can't think of a way to fix without writing a super complicated onLayout handling.

I am aware that the Text component has press handling, but between the horrible delayed visual feedback and the fact that it doesn't support onLongPress or onPressIn, this route is unworkable in my case.

Does anyone know how to fix this issue?

CodePudding user response:

I can recommend you a package that I recently used to separate url, hashtag and label in a social media application.

https://www.npmjs.com/package/react-native-parsed-text

With this package, you can customize only many of your items and change the click events.

const hashtagPattern = /(#(?:[\w\ığüşöçİĞÜŞÖÇ] ))/g;
const hashtagRegex = new RegExp(hashtagPattern);

const labelPattern = /(@(?:[\w\ığüşöçİĞÜŞÖÇ] ))/g;
const labelRegex = new RegExp(labelPattern);

const urlPattern =
  /[-a-zA-Z0-9@:%_\ .~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\ .~#?&//=]*)?/g;
const urlRegex = new RegExp(urlPattern);

<ParsedText
    style={{ color: '#000' }}
    parse={[
            {
                pattern: hashtagPattern,
                style: {color: 'red'},
                onPress: () => {},
                onLongPress: () => {},
                // Custom render
                // renderText: () => {}
            },
            {
                pattern: labelPattern,
                style: {color: 'blue'},
                onPress: () => {}
            },
            {
                pattern: urlPattern,
                style: {color: 'green'},
                onPress: () => {}
            },
        ]}>
        Hello World #hello
</ParsedText>

CodePudding user response:

This is by no means a perfect solution, but I found a workaround. If you use translate to force the Touchable wrapped text back into place (by some constant factor of the line height) it produces good, consistent results. If anyone finds a better real solution, I will update.

  • Related