'react native touchable highlight and touchable native feedback for making my menu items

I am trying to implement a basic drawer, but I am confused by the documentation.

There is a TouchableHighlight, and there is a TouchableNativeFeedback.

TouchableNativeFeedback is Android only. So, how can I best handle my MenuItem component so it takes care of both Android and IOS?

Here's what I've got so far, and I'm not sure how to handle the different platform specific touchable stuff here:

import React, { Component, PropTypes } from 'react';
import { TouchableHighlight, TouchableNativeFeedback, Platform, View, Text } from 'react-native';

export class MenuItem extends Component {

  handleAndroid() {

  }

  handleIOS() {

  }

  renderMenuItem() {
    return (
      <View style={styles.container}>
        <Text style={styles.text}>
          {this.props.text}
        </Text>
      </View>
    );
  }
  render() {
    return (

    );
  }
}

On Android, do I have to use TouchableNativeFeedback only?



Solution 1:[1]

You can solve this in a more elegant manner like this:

render() {
    let TouchablePlatformSpecific = Platform.OS === 'ios' ? 
        TouchableOpacity : 
        TouchableNativeFeedback;

    let touchableStyle = Platform.OS === 'ios' ? 
        styles.iosTouchable : 
        styles.androidTouchable

    return(
        <TouchablePlatformSpecific style={touchableStyle}>
            (...)
        </TouchablePlatformSpecific>   
    );
}    

I've been using this successfully, and I fail to see a reason it would not work, it looks good to me.

Solution 2:[2]

A better implementation would be to have dumb component that produces generic Touchable that works depending upon the platform and handle onPress events.

Touchable Component:

import { Platform } from "react-native"


import { TouchableNativeFeedback, TouchableOpacity } from "react-native"
import React from "react"

export const Touchable = (props: any) => {
  return Platform.OS === 'android'
    ? <TouchableNativeFeedback onPress={props.onPress}>{props.children}</TouchableNativeFeedback>
    : <TouchableOpacity onPress={props.onPress}>{props.children}</TouchableOpacity>
}

Usage:

import { Touchable } from '../path/to/touchable.component';
...
render() {
    <Touchable onPress={() => this.handleClick()}>
         .....
    </Touchable>
}

Thanks to @Giorgos' implementation which formed the basis for this answer.

Solution 3:[3]

Or you could create a custom component like this:

import { TouchableNativeFeedback, TouchableOpacity } from 'react-native'
...
const Touchable = (props) => {
  return Platform.OS === 'android'
    ? <TouchableNativeFeedback>{props.children}</TouchableNativeFeedback>
    : <TouchableOpacity>{props.children}</TouchableOpacity>
}

And then use it like this:

<Touchable>
    <Card>
       <Text>Click me!</Text>
    </Card>
</Touchable>

Solution 4:[4]

I also have nearly the same requirements and I ended up using this library react-native-haptic-feedback.

Keep in mind that haptic feedback is available only on some latest android devices and in iOS above iPhone 6s. For devices without haptic motor, there will be vibration. Here is a sample code snippet:

import ReactNativeHapticFeedback from "react-native-haptic-feedback";

const options = {
  enableVibrateFallback: true,
  ignoreAndroidSystemSettings: false
};

ReactNativeHapticFeedback.trigger("impactMedium", options);

In your case, it will work directly with the button's onPress method such as:

<TouchableOpacity onPress={()=>{ReactNativeHapticFeedback.trigger("impactMedium", options);}} />

Note: I found that the most versatile type which is supported by maximum devices is impactMedium.

Solution 5:[5]

Based on @Nithish JV and Giorgos Karyofyllis:

import React from "react"
import { Platform } from "react-native"
import { TouchableNativeFeedback, TouchableOpacity } from "react-native"

export const TouchableFeedback = (props: any) => {
    const { children, ...rest } = props;
    return Platform.OS === 'android'
        ? <TouchableNativeFeedback {...rest}>{children}</TouchableNativeFeedback>
        : <TouchableOpacity {...rest}>{children}</TouchableOpacity>
}

Solution 6:[6]

The best elegant way to do it

import { TouchableNativeFeedback, TouchableOpacity } from 'react-native'

in render:

TouchableButton = Platform.OS === 'android' ? TouchableNativeFeedback : TouchableOpacity
return (
    <TouchableButton>{'bla bla'}</TouchableButton>
)

Solution 7:[7]

Since the operating system doesn't change magically during runtime, I always use this little snippet:

import { Component } from "react"
import {
    Platform,
    TouchableNativeFeedback,
    TouchableOpacity,
} from "react-native"

// The operating system doesn't magically change,
// so we can call the function directly to get the correct component
export default ((): typeof Component => {
    if (Platform.OS === "android") {
        return TouchableNativeFeedback
    }

    return TouchableOpacity // Choose whatever touchable you like
})()

It's basically an IIFE that selects the correct touchable for the operating system.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Lucas Franceschi
Solution 2 Nithish JV
Solution 3 Giorgos Karyofyllis
Solution 4 Umang Loriya
Solution 5 Dennis
Solution 6 Idan
Solution 7 Myzel394