I'm having an intermittent problem with the TouchableOpacity component in react-native.
I basically click on the element, it performs the opacity animation normally, but my console.log runs randomly. In iOS works correctly.
My code:
<TouchableOpacity style={{backgroundColor:'red'}} onPress={async () => {
console.log('Button '+item.tag+' pressed.');
}}>
NOTE: I've tried calling this function without async.
I found the solution!
PanResponder was detecting micro movements on simple touch. In the onMoveShouldSetPanResponder parameter I defined the following function:
panResponder = PanResponder.create({
onMoveShouldSetPanResponder: (e, g) => {
return Math.abs(g.dx) > 2 || Math.abs(g.dy) > 2;
},
...
});
Related
TouchableOpacity onPress is not working inside Flatlist but when I replaced onPress with onPressIn/onPressOut it is working fine, but in that case the reaction is too fast and having issue while scrolling. I don''t know what it is happening and haven't found any related issue. Below is my code:
renderItem = ({ item, index }: { item: any, index: number }) => {
const { type } = this.props;
const valueType = {
phone: item,
stage: item.title,
location: item.name
}
return (
<TouchableOpacity
onPressIn={() => this.onSelect(item, index)}
style={styles.modalListContainer}
>
<Icon name={icon[type]} height={20} width={20}/>
<Spacer width={10} />
<View style={styles.modelTextContainer}>
<Text style={styles.modelText}>{valueType[type]}</Text>
</View>
</TouchableOpacity>
)
}
<FlatList
data={item}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
ItemSeparatorComponent={() => <><Spacer height={10} /><View style={styles.modelTextDevider} /><Spacer height={10} /></>}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.container}
/>
It is rendered inside a Modal using react-native-modals library. Any help would be appreciated.
Thank you.
react-native-modals, have a parent touchable component (PanResponder) which wraps your children's components. On some android devices, when you have a touchable component like a button, the touch event does not propagate down to child component instead capture by react-native-modals parent component.
The ideal solution should be absolute positioning your button but will break your UI and the modal will be useless.
There's an existing issue with this library repository.
https://github.com/jacklam718/react-native-modals/pull/210
but the solution provided is not 100% accurate for Android devices.
If you're using React Navigation, you already installed react-native-gesture-handler.
import TouchableOpacity from react-native-gesture-handler in place of `react-native. It should solve the issue for most devices.
I am trying to build an auth flow that has a welcome page and then login or signup depending on the user's case.
It is built inside a stack navigator. The first screen has no header but then login and signup do via their screen options.
// AuthNavigator
const AuthStackNavigator = createStackNavigator();
export const AuthNavigator = () => {
return (
<AuthStackNavigator.Navigator
initialRouteName={WELCOME_PAGE.id}
screenOptions={{
headerShown: false,
headerTintColor: colors.primary,
}}
lazy>
<AuthStackNavigator.Screen
name={WELCOME_PAGE.id}
component={WELCOME_PAGE.component}
/>
<AuthStackNavigator.Screen
name={LOGIN_PAGE.id}
component={LOGIN_PAGE.component}
options={LOGIN_PAGE.options}
/>
<AuthStackNavigator.Screen
name={SIGN_UP_PAGE.id}
component={SIGN_UP_PAGE.component}
options={LOGIN_PAGE.options}
/>
</AuthStackNavigator.Navigator>
);
};
This flow, is nested inside a tabNavigator:
// AuthTabNavigator
const AuthTabNavigator =
Platform.OS === 'android'
? createMaterialBottomTabNavigator()
: createBottomTabNavigator();
const RootNavigator = () => {
return (
<AuthTabNavigator.Navigator
activeColor={colors.primary}
inactiveColor="grey"
barStyle={materialBottomNavBackgroundColor}
tabBarOptions={defaultTabNavOptions}>
<AuthTabNavigator.Screen
name={WELCOME_PAGE.id}
component={AuthNavigator}
options={WELCOME_PAGE.options}
/>
</AuthTabNavigator.Navigator>
);
};
export default RootNavigator;
On iOS, things work fine but on Android, It has weird behaviour. When pressing on the input field, the keyboard pushes the bottom bar causing a clunky effect on press but also on dismiss of the field. As if it were recalculating the height every time and repositioning the layout.
In order to make sure it's not coming from my code, I tested again with the snippet from the React Native documentation
I get the following (these are on the Android emulator but I get the same result on my OnePlus android phone)
base version: https://recordit.co/tcEwDbo1oT
no header version: https://recordit.co/O4lZ9G83vg
not nested in a tab navigator: https://recordit.co/uh7mOGlKdk
The only version that works is the one when not nested in the tab navigator so I guess the issue comes from there.
I checked a few solutions but none worked:
Android manifest
Issue coming from the header
Another
workaround because of header
I am using React Native CLI:
"react-native": "0.62.2",
"#react-navigation/bottom-tabs": "^5.2.8",
"#react-navigation/drawer": "^5.7.2",
"#react-navigation/material-bottom-tabs": "^5.1.10",
"#react-navigation/native": "^5.1.7",
"#react-navigation/stack": "^5.2.17",
Let me know if you encountered the same issue and found a way to fix it. Thanks in advance.
EDIT: SOLUTION I WENT WITH
On top of the "custom" solution I posted in the comments, after opening an issue on React Navigation repo and checking closed issues, I found that on Android, you might have 2 options:
Either your invert the nesting, see here
Or you can set the behaviour of the keyboardAvoidingView to position on Android and provide a custom Tabbar
https://github.com/react-navigation/react-navigation/issues/7359#issuecomment-545842090
https://stackoverflow.com/a/51169574/11287266
I ended up going with the latter:
const TabBarComponent = props => {
return (
<View collapsable={false}>
<BottomTabBar {...props} />
</View>
);
};
// AuthTabNavigator
const AuthTabNavigator = createBottomTabNavigator();
const RootNavigator = () => {
return (
<AuthTabNavigator.Navigator
activeColor={colors.primary}
inactiveColor="grey"
barStyle={materialBottomNavBackgroundColor}
tabBarOptions={defaultTabNavOptions}
tabBar={props => <TabBarComponent {...props} />}>
<AuthTabNavigator.Screen
name={WELCOME_PAGE.id}
component={AuthNavigator}
options={WELCOME_PAGE.options}
/>
</AuthTabNavigator.Navigator>
);
};
export default RootNavigator;
<KeyboardAvoidingView
contentContainerStyle={{ flexGrow: 1 }}
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'position'}
keyboardVerticalOffset={Platform.OS === 'android' ? -50 : 25}>
I am working on some end-to-end tests for a React Native (version 0.60) application which are run via Appium.
I have a couple of buttons which are wrapped around a SafeAreaView to avoid problems with the latest iOS devices (e.g. iPhone X, iPad Pro, etc...). This is the key part of the Component render() function:
const Buttons = (
<StickyContainer visible={isSizeSelected} width={width} style={containerStyle}>
{showAddToWishlist && (
<Button
outline
fixedWidth
uppercase
tx="product.addToWishlist"
onPress={() => this.onPressAddTo(ProductAction.AddToWishlist)}
icon="heartBlack"
margin={margin}
showSpinner={isAddingToWishlist}
/>
)}
{showAddToShoppingBag && (
<Button
primary
fixedWidth
uppercase
tx="product.addToCart"
onPress={() => this.onPressAddTo(ProductAction.AddToShoppingBag)}
showSpinner={isAddingToShoppingBag}
{...setTestId("sizeOverlayAddToCartButton")}
/>
)}
</StickyContainer>
)
return <SafeAreaView forceInset={{ bottom: "never" }}>{Buttons}</SafeAreaView>
As you can see, the accessibility IDs are set through the setTestid() function which is doing nothing more than this:
const getPlatformTestId = (id: string) => {
if (IS_IOS) {
return {
testID: id
}
}
return {
accessibilityLabel: id,
accessible: true
}
}
export const setTestId = (id: string) => {
return getPlatformTestId(id)
}
Now the problem: if I run the app and I try to search on Appium for the ID sizeOverlayAddToCartButton I can't find anything. If I remove the <SafeAreaView> and I return directly Buttons the ID is found without any problem.
It's also interesting that if I use the app Accessibility Inspector (it's part of Xcode) instead of Appium, the ID is always found no matter if I use the <SafeAreaView>.
Does someone know why this is not working? I can't find any compatibility issue online
I'm using Flatlist in HomeScreen and it shows me multiple posts. So now what I want is whenever I Signout from the app or close the app and then open the app, I should be able to see the first item from Flatlist.
I've tried using scrollToIndex inside my render function but it gave me an error - undefined is not an object (evaluating '_this2.refs.flatListRef.scrollToIndex')
<FlatList
data={this.state.data}
ref={(ref) => { this.flatListRef = ref; }}
renderItem={ ({item,index}) => this._renderItem(item,index) }
extraData={[ this.state.data, this.state.checked ]}
/>
And this is what I tried using in componentDidMount and inside render function this.refs.flatListRef.scrollToIndex({animated: true,index:0}); but didn't work.
ComponentDidMount callbacks won't run after the user leaves the app and resumes it later. You have to use AppState instead:
AppState can tell you if the app is in the foreground or background, and notify you when the state changes. [Source]
Adapt the given example to your needs and scroll with this.flatListRef.scrollToIndex({animated: true,index:0})}
import {
ScrollView,
} from 'react-native';
import {useScrollToTop} from '#react-navigation/native';
const ref = React.useRef(null);
useScrollToTop(ref);
const goToTopClickAction = () => {
ref.current?.scrollTo({
y: 0,
animated: true,
});
};
add ref={ref} in ScrollView tag
< ScrollView ref={ref}> < /ScrollView >
I am running react native 0.24.1 and I am experiencing an issue with the <TouchableOpacity> component when it is placed inside an <ScrollView>.
Its onPress events fire fine but there is a special case when they do not.
If along with the <TouchableOpacity> component you have a <TextInput>, and the current focus is on the <TextInput> box, then you may click on the <TouchableOpacity> and you will see its onPress event WILL NOT be fired.
At least the first time you do it. Once the focus is NOT on the <TextInput> anymore, you can now press on the <TouchableOpacity> component and its onPress event will fire just fine.
Note that if the <TouchableOpacity> component is placed inside a <View> instead of an <ScrollView> everything works as expected and the above issue does not apply.
Here is some code to demonstrate the problem:
const React = require('react-native');
const {
Component,
Dimensions,
View,
ScrollView,
Text,
TextInput,
TouchableOpacity,
} = React;
// ----------------------------------------------------------------------------
class TouchableOpacityTest extends Component {
constructor(props, context) {
super(props, context);
this.state = {count_onPress:0,count_onPressIn:0,count_onPressOut:0,count_onLongPress:0};
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
onPressEvent(what,e) {
console.log('what:',what);
let newState = {};
newState['count_'+what] = ++this.state['count_'+what];
this.setState(newState);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
render() {
let touchableProps = {
onPress: this.onPressEvent.bind(this,'onPress'),
onPressIn: this.onPressEvent.bind(this,'onPressIn'),
onPressOut: this.onPressEvent.bind(this,'onPressOut'),
onLongPress: this.onPressEvent.bind(this,'onLongPress'),
}
return (
<View style={{flex:1,flexDirection:'column',justifyContent:'flex-start',alignItems:'center',backgroundColor:'blue'}} >
<ScrollView style={{width:Dimensions.get('window').width*0.9,backgroundColor:'red'}}>
<TextInput style={{backgroundColor:'rgb(200,200,200)',marginTop:14}}
placeholder="Focus on me,hide keyboard,and click on text below"
autoCorrect={false}
/>
<TouchableOpacity {...touchableProps} >
<Text style={{fontSize:20,backgroundColor:'pink',marginTop:14}}>
Click on me!{"\n"}
onPress:{this.state.count_onPress}{"\n"}
onPressIn:{this.state.count_onPressIn}{"\n"}
onPressOut:{this.state.count_onPressOut}{"\n"}
onLongPress:{this.state.count_onLongPress}{"\n"}
</Text>
</TouchableOpacity>
</ScrollView>
</View>
);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
// ----------------------------------------------------------------------------
AppRegistry.registerComponent('react_native_app1', () => TouchableOpacityTest);
You may replace the <ScrollView> with a <View> component on the above code and you will see that onPress event fires every time, even when the focus is on the <TextView>
NOTE: I am working on Android. I have no idea if this happens also on iOS.
NOTE 2: According to Aakash Sigdel, this is indeed happening on iOS too.
Set keyboardShouldPersistTaps={true} on your ScrollView.
Duplicate answer here: https://stackoverflow.com/a/34290788/29493
UPDATE: As Hossein writes in his answer, true|false has been deprecated in newer versions in favor of always|never|handled.
Set keyboardShouldPersistTaps='always' to your ScrollView props.
React Native Documentation:
'never' (the default), tapping outside of the focused text input when the keyboard is up dismisses the keyboard. When this happens, children won't receive the tap.
'always', the keyboard will not dismiss automatically, and the scroll view will not catch taps, but children of the scroll view can catch taps.
'handled', the keyboard will not dismiss automatically when the tap was handled by a children, (or captured by an ancestor).
false, deprecated, use 'never' instead.
true, deprecated, use 'always' instead.
In my case, I was using alignItems:'baseline', when I switched to alignItems:'center', it started working smoothly. Don't know why