React Native onLongPress triggered onPress if pressed component is re-rendered - react-native

I am implementing an image gallery. There's a list of squared images, if user longPress an image component it will re-render to a selected image component which show a blur background and a tick on top of the image.
Here's the normal image component:
<TouchableOpacity
key={index}
onPress={this.normalModeImgClick(img, index)}
onLongPress={this.startSelectMode(index)}
>
<Image style={styles.img} source={{uri: img.resized_xs_url}}/>
</TouchableOpacity>
Here's the component at select mode:
<TouchableOpacity
key={itemIndex}
style={styles.selectedImgWrapper}
onPress={this.selectModeImgClick(imgItem, itemIndex)}
>
<Image style={styles.img} source={{uri: imgItem.img.resized_xs_url}}/>
{imgItem.selected &&
<View style={styles.selectedImgCover}>
<Image style={styles.selectedIcon} source={require('../../assets/icon_tick.png')}/>
</View>
}
</TouchableOpacity>
As you can see, if you longPress a normal image component startSelectMode will trigger, and that image will re-render and turned to a select mode component. However, selectModeImgClick will also be triggered which it is not supposed to (as the user is still doing the longPress action).
How to prevent this happening?

Accidentally discovered an alternative right after I posted the question, solved by adding an empty onLongPress function props to the selected mode component like this:
<TouchableOpacity
key={itemIndex}
style={styles.selectedImgWrapper}
onPress={this.selectModeImgClick(imgItem, itemIndex)}
// Override 'onLongPress' with an empty function
onLongPress={() => {}}
>
<Image style={styles.img} source={{uri: imgItem.img.resized_xs_url}}/>
{imgItem.selected &&
<View style={styles.selectedImgCover}>
<Image style={styles.selectedIcon} source={require('../../assets/icon_tick.png')}/>
</View>
}
</TouchableOpacity>
Since the component is not designed to have a longPress action, I'm looking forward to better solutions:

Related

Why does TouchableOpacity automatically trigger onPress in ReactNative Expo Snack?

I have a small ReactNative app in Expo Snack that includes a single components with a TouchableOpacity.
export default function AssetExample() {
function clicked() {
alert('clicked')
}
return (
<View style={styles.container}>
<TouchableOpacity onPress={clicked()}>
<Text style={styles.paragraph}>
Click the icon
</Text>
<Image style={styles.logo} source={require('../assets/snack-icon.png')} />
</TouchableOpacity>
</View>
);
}
Every time the app is started the alert is shown, meaning TouchableOpacity triggers onPress. Why? And how can I circumvent this?
You should pass the reference of your function to TouchableOpacity like this
<TouchableOpacity onPress={clicked}>
</TouchableOpacity>
or pass it with arrow function
<TouchableOpacity onPress={()=> clicked()}>
</TouchableOpacity>
so it will be called inside TouchableOpacity component whenever required (button click), otherwise it will be called only one time during render phase.
See these docs for handling events in React https://reactjs.org/docs/handling-events.html

Present modal inside a screen

I'm creating an application where I'm using a MaterialBottomTabBar and in the Home Screen I've a button which are supposed to open up a <Modal>.
After investigating this for quite some time I found out that I cannot present the modal when using the tabbar. I've tried to put the modal in all my tabbar-screens (without any success). And then moved the modal over to another screen which are not included in the tab-bar and the modal worked perfectly fine.
export default function Home({ route, navigation }){
const [modal, setModal] = useState(false)
return(
<View style={styles.container}>
<Modal visible={modal} transparent={true}>
<View style={{height: "100%", width: "80%", backgroundColor: "blue"}}>
<Text>TIFMNASIFAMNFISAMFIAOFMSAOFMAFOPSAMF</Text>
</View>
</Modal>
<TouchableOpacity style={styles.addNewButton} onPress={() => setModal(true)}>
<Text style={styles.addNewButtonText}>+</Text>
</TouchableOpacity>
</View>
)
}
As you can see i'm using the React Native Hooks useState(false) and refers to it inside the Modal. But whenever I tap the <TouchableOpacity> the modal is not being presented.
NOTE!
The modal is being presented if I replace the
<Modal visible={modal} transparent={true}>
to
<Modal visible={true} transparent={true}>
But would be runned instantly, which is not an option in my case.
Do you have any idea how I can run this modal inside the screen?

How to create swipeable bottom sheet in React Native?

How to implement exactly the same swipeable bottom sheet in React Native:
react-swipeable-bottom-sheet (demo gif above) lib is working only in React.
I know react-native-overlay-section but it is opening to the top of the window with bounce animation.
EDIT
I also tried react-native-touch-through-view, but there is a known issue with touches through the TouchThroughView...
<View style={styles.container}>
<View style={styles.contentContainer}>
<FlatList
data={this.state.list}
renderItem={(data) => {
<View>
<Text>{"Bla bla bla"}</Text>
<TouchableOpacity onPress={()=> this.onPress()}>
<Text>{"THIS IS NOT WORKING ON ANDROID"}</Text>
</TouchableOpacity>
</View>
}} />
</View>
<TouchThroughWrapper style={styles.scrollWrapper}>
<ListView
style={styles.scroller}
dataSource={actionsList}
renderHeader={() => <TouchThroughView style={styles.touchThroughView} />}
renderRow={(rowData) => {
return (
<Text style={styles.itemRow}>{rowData}</Text>
)
}}>
</ListView>
</TouchThroughWrapper>
You can achieve your requirement with the help of following package.
https://www.npmjs.com/package/react-native-touch-through-view
This package allows scroll views and table views to scroll over interactable content without poor performing size and bounds animations.

How to correct event propagation in case of multilayer component

I have 2 View component rendered over each other using absolute positioning. Both view have onPress events binded using TouchableOpacity. Now when I press top view actually below view onPress event is triggered and top one remains as it is. What am I doing wrong here?
Top Layer Code
<TouchableOpacity style={cell} onPress={this.handleMessangerPress}>
<Image style={[filterImage, {marginLeft: 20}]} source={{ uri: imagePath + 'messanger.png' }} />
</TouchableOpacity>
Bottom Layer Code
<TouchableOpacity style={styles.subTotal} onPress={this.handleSubTotalPress}>
<View>
<Text style={styles.text}>{'Subtotal Rs.' + total}</Text>
</View>
</TouchableOpacity>
In above case handleSubTotalPress is getting called in place of handleMessangerPress
Use this way
<TouchableOpacity style={styles.subTotal} onPress={()=> this.handleSubTotalPress() }>
or
<TouchableOpacity style={styles.subTotal} onPress={this.handleSubTotalPress.bind(this) }>

React Native detect tap on View

I’m writing a React Native app and I want to detect tap/clicks anywhere on the screen so I put an onClick handler on my <View> element but it doesn’t seem to be working. Here is my render function:
<View style={styles.container} onClick={this.onClick}>
<Text style={styles.welcome}>
Tap to change the background
</Text>
</View>
What do I need to do?
For making any element to handle touch/click events in a React-Native UI, you need to put the element inside a TouchableOpacity, TouchableWithoutFeedback, TouchableNativeFeedback or TouchableHighlight element:
<TouchableHighlight onPress = { this.onClick }>
<View style={styles.container}>
<Text style={styles.welcome}>
Tap to change the background
</Text>
</View>
</TouchableHighlight>
Hope that helps.
In React Native version > 55.3 you make check onPress events into your View if use onStartShouldSetResponder props.
Like example:
<View
style={{ flex: 1 }}
onStartShouldSetResponder={() => console.log('You click by View')}
>
<ScrollView
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this.onRefresh} />
}
>
<Text>Awesome</Text>
</ScrollView>
</View>
In example I showed how you can get onPress event on View and not blocking other event behind them. Refresh control still work correct.
In my case the following works fine. You can use onTouchStart and onTouchEnd handlers on any View via props, for example:
<View onTouchStart={() => this.doSomething()} />
More information
This worked for me...
onTouchEnd={() => alert('TAPPED')}
The easiest way to do that:
<TouchableOpacity style={styles.container} onPress={()=> whateverFunc(parameter)}>
<Text style={styles.welcome}>
Tap to change the background
</Text>
</TouchableOpacity>
So, you need to replace the 'View' component to a 'TouchableOpacity' or any other Touchable component and you also need to replace the 'onClick' prop to 'onPress'. That's all. The wrapping of a view with a TouchableWhatever component is not necessary at all.