react-native TouchableNativeFeedback onPress not working - react-native

I have created a composed component to compose TouchableNativeFeedback to wrapperComponent.
export default function withFeedback2(
WrappedComponent
) {
return class extends BaseComponent {
constructor(props) {
super(props);
}
render() {
return (
<View>
<TouchableNativeFeedback
onPress={() => this.props.onContainerViewPress()}
>
<WrappedComponent {...this.props} />
</TouchableNativeFeedback>
{/* <TouchableOpacity
onPress={this.props.onContainerViewPress ? () => this.props.onContainerViewPress() : null}
>
<WrappedComponent {...this.props} />
</TouchableOpacity> */}
</View>
);
}
};
}
But OnPress event of TochableNativeFeedback is not firing. Whereas OnPress event is fired correctly and onContainerViewPress prop of wrappercomponent is called if wrappercomponent wrapped under TouchableOpacity.
I am testing this on the Android Platform.

Use a <View></View> to wrap your WrappedComponent for TouchableNativeFeedback.
<TouchableNativeFeedback
onPress={() => this.props.onContainerViewPress()}>
<View>
<WrappedComponent {...this.props} />
</View>
</TouchableNativeFeedback>

There are two different TouchableNativeFeedback classes. Make sure you import the correct one:
import { TouchableNativeFeedback } from "react-native"
import { TouchableNativeFeedback } from "react-native-gesture-handler"
I had a similar problem and finally used it from "react-native" library. Importing it from "react-native-gesture-handler" did not work for me.

I've discovered that adding a Ripple effect to the TouchableNativeFeedback fixes the issue for me:
background={TouchableNativeFeedback.Ripple("#FFFFFF",true)}
i.e.
export default function withFeedback2(
WrappedComponent
) {
return class extends BaseComponent {
constructor(props) {
super(props);
}
render() {
return (
<View>
<TouchableNativeFeedback
onPress={() => this.props.onContainerViewPress()}
background={TouchableNativeFeedback.Ripple("#FFFFFF",true)}
>
<WrappedComponent {...this.props} />
</TouchableNativeFeedback>
</View>
);
}
};
}

Try: useForeground={true}
<TouchableNativeFeedback onPress={() => {}} useForeground={true}>

You can call method as below:
export default function withFeedback2(
WrappedComponent
) {
return class extends BaseComponent {
constructor(props) {
super(props);
this.onContainerViewPress = this.onContainerViewPress.bind(this);
}
onContainerViewPress() {
const { onContainerViewPress } = this.props;
onContainerViewPress();
}
render() {
return (
<View>
<TouchableNativeFeedback
onPress={() => { this.onContainerViewPress(); }}
>
<WrappedComponent {...this.props} />
</TouchableNativeFeedback>
{/* <TouchableOpacity
onPress={this.props.onContainerViewPress ? () => this.props.onContainerViewPress() : null}
>
<WrappedComponent {...this.props} />
</TouchableOpacity> */}
</View>
);
}
};
}

Try to import Touchable native feedback from react native gesture handler library

import { TouchableNativeFeedback } from "react-native"
import { TouchableNativeFeedback } from "react-native-gesture-handler"
Supplementing the answer of mangei the problem could be if you import it from the wrong place. You have to import it from react-native-gesture-handler if you are inside a gesture handler (NOTE: react-navigation's TabBar itself has a PanGestureHandler in it by default). What react-native-gesture-handler does is it wraps components like ScrollView or TouchableNativeFeedback with its own implementation to be able to handle gestures inside the GestureHandler as well that are "not meant" for the GestureHandler but rather for the ScrollView or the TouchableNativeFeedback. If you're inside the gesture handler, you have to import it from react-native-gesture-handler else from react-native.

Related

Why doesn't checkbox work in React Native?

import CheckBox from '#react-native-community/checkbox';
export default class All extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
isSelected: true,
};
}
checkBoxChanged() {
alert('changed');
this.setState({isSelected : !this.state.isSelected})
}
render() {
const { items } = this.state;
return (
<Content>
<View>
{items.map((item) => (
<View>
<Text>{item.name}</Text>
<CheckBox
value={this.state.isSelected}
onValueChange={() => this.checkBoxChanged()}
/>
</View>
))}
</View>
</Content>
);
}
}
This doesn't work.I mean nothing happens.
When I check on, nothing changes and it doesn't reach to checkBoxChanged().
I got stuck in this problem.
I would appreciate it if you could help me :)
You can use onValueChange={() => checkBoxChanged()}
<CheckBox
value={this.state.isSelected}
onValueChange={() => checkBoxChanged()}
/>
And in checkBoxChanged function you can set the state to change the value of isSelected
checkBoxChanged(){
this.setState({isSelected : !this.state.isSelected})
}
Moreover the checkbox has been deprecated you have to install
#react-native-community/checkbox
check this link to know more.
Hope this helps
import { CheckBox } from 'react-native';
isChecked = false
checkBoxChanged() {
alert('changed');
}
getCheckedStatus(){
this.isChecked != this.isChecked;
return this.isChecked;
}
<CheckBox
activeOpacity={1}
textStyle={{ color: colors.colorGray, fontSize: dimen.fontSize.textAppearanceBody1_16 }}
containerStyle={styles.checkBoxContainer}
checkedColor={colors.profileTabSelectedColor}
uncheckedColor={colors.profileTabSelectedColor}
title={'Gender'}
checked={this.getCheckedStatus()}
onPress={() => { this.checkBoxChanged() }}
/>
I would like to tell you don't use checkbox from react-native it is deprecated, still if you are using it please see the below code, there is no onChange Prop, use onValueChange instead of onChange, and maintain a state and pass value prop to checkbox component.
https://reactnative.dev/docs/checkbox#__docusaurus
import React, { useState } from "react";
import { CheckBox, Text, StyleSheet, View } from "react-native";
export default App = () => {
const [isSelected, setSelection] = useState(false);
return (
<View style={styles.container}>
<View style={styles.checkboxContainer}>
<CheckBox
value={isSelected}
onValueChange={setSelection}
style={styles.checkbox}
/>
<Text style={styles.label}>Do you like React Native?</Text>
</View>
<Text>Is CheckBox selected: {isSelected ? "👍" : "👎"}</Text>
</View>
);
};
The code should be
import { CheckBox } from 'react-native';
checkBoxChanged() {
alert('changed');
}
<CheckBox onValueChange={()=>this.checkBoxChanged()} />
You are calling the function directly instead of calling it on click.
This checkbox works only in Android so better use the one from react-native elements
https://react-native-elements.github.io/react-native-elements/docs/checkbox.html
Checkbox are being deprecated from react-native-element but it can be used from react native component.
to use them.
Install : npm install #react-native-community/checkbox --save
Usage:
import CheckBox from '#react-native-community/checkbox';
Inside you use this element
<CheckBox value={this.state.check}
onChange={()=>this.checkBoxText()} />
And inside your class
constructor(props) {
super(props);
this.state = {
check: false
};
Declare a function outside your constructor area.
create:
checkBoxText() {
this.setState({
check:!this.state.check
})
alert("Value Changed to " + this.state.check)
}
It will create your clickable Check-box on your application.

Access to this.props.navigation on Header Component

I have an ApplicationHeader Component and I want to navigate to a specific screen on touch, but I'm getting
undefined is not an object (evaluating this.props.navigation.navigate)
App.js
render() {
return (
<SafeAreaView forceInset={{ bottom: 'never' }} style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
<Provider store={store}>
<ApplicationHeader navigation={this.props.navigation} />
<AppNavigator />
</Provider>
</SafeAreaView>
);
}
ApplicationHeader.js
class ApplicationHeader extends Component {
constructor(props) {
super(props)
}
openWishlist() {
this.props.navigation.navigate('Wishlist')
}
render() {
const { isLogged } = this.props;
return (
<View style={AppStyle.header}>
<TouchableOpacity onPress={() => this.openWishlist()}>
{!isLogged && (<Image source={imgWishList} style={AppStyle.headerWishlist} />)}
{isLogged && (<Image source={imgWishListLogged} style={AppStyle.headerWishlist} />)}
</TouchableOpacity>
</View>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ApplicationHeader);
It seems like that your HeaderComponent is might not be in Navigation Stack.
In that case, you can try the following solution:
First import withNavigation in your file like
import { withNavigation } from 'react-navigation';
After that change your component export to
export default connect(
mapStateToProps,
mapDispatchToProps
)(withNavigation(ApplicationHeader));
So you will get the access to navigation pros in this component.
Also your ApplicationHeader can move to inner container components. It shouldn't have to be in App.js to use the navigation props.
Hope this will help you.

Navigation.getParam is undefined while trying to pass function as parameter

I'm trying to use a function from my Main component in my details component which I user react navigation to navigate to and I want to save some changes in detail screen in my main component
//Main.js
import React from 'react';
import {
StyleSheet ,
Text,
View,
TextInput,
ScrollView,
TouchableOpacity,
KeyboardAvoidingView,
AsyncStorage
} from 'react-native'
import Note from './Note'
import { createStackNavigator, createAppContainer } from "react-navigation";
import Details from './Details';
export default class Main extends React.Component {
static navigationOptions = {
title: 'To do list',
headerStyle: {
backgroundColor: '#f4511e',
},
};
constructor(props){
super(props);
this.state = {
noteArray: [],
noteText: ''
};
}
render() {
let notes = this.state.noteArray.map((val,key) => {
return <Note key={key} keyval={key} val={val}
goToDetailPage= {() => this.goToNoteDetail(key)}
/>
});
const { navigation } = this.props;
return(
<View style={styles.container}>
<ScrollView style={styles.scrollContainer}>
{notes}
</ScrollView>
<Details saveEdit={this.saveEdit} />
</View>
);
}
goToNoteDetail=(key)=>{
this.props.navigation.navigate('DetailsScreen', {
selectedTask: this.state.noteArray[key],
saveEdit: this.saveEdit
});
}
saveEdit = (editedTask,dueDate) => {
this.state.noteArray.push({
'creationDate': editedTask['creationDate'],
'taskName': editedTask['taskName'],
'dueDate': dueDate
});
this.setState({noteArray:this.state.noteArray})
this.saveUserTasks(this.state.noteArray)
}
this.setState({noteArray:this.state.noteArray})
this.saveUserTasks(this.state.noteArray)
}
}
Then I try to use it as prop in my Detail.js
import React from 'react';
import {
StyleSheet ,
Text,
View,
TextInput,
Button,
TouchableOpacity,
} from 'react-native'
import { createStackNavigator, createAppContainer } from "react-navigation";
export default class Details extends React.Component {
constructor(props){
super(props);
this.state = {
dueDate = ''
}
}
static navigationOptions = {
headerStyle: {
backgroundColor: '#f4511e',
},
};
componentDidMount = () => {
this.getUserTasks()
}
render() {
const { navigation } = this.props;
const selectedTask = navigation.getParam('selectedTask', 'task');
var { saveEdit} = this.props;
return(
<View key={this.props.keyval} style={styles.container}>
<View style = { styles.info}>
<Text style= {styles.labelStyle}> Due date:
</Text>
<TextInput
onChangeText={(dueData) => this.setState({dueData})}
style={styles.textInput}
placeholder= {selectedTask['dueDate']}
placeholderTextColor='gray'
underlineColorAndroid = 'transparent'
>
</TextInput>
</View>
<TouchableOpacity onPress={this.props.saveEdit(selectedTask, this.state.dueDate)} style={styles.saveButton}>
<Text style={styles.saveButtonText}> save </Text>
</TouchableOpacity>
</View>
);
}
}
I searched a lot to find the solution and I tried many of them but get different undefined errors. This is not what I did in the first place but when I search I found this solution here. And I know it causes lots of issues.
I want to know how can I manage to access to main method from details and pass parameters to it or how can I manage to use main props in my details component
If you are using react-navigation 5, params is no longer under the navigation object but under route object. This is the link to the sample code:
https://reactnavigation.org/docs/params
Solution
<Details saveEdit={this.saveEdit} />
to
<Details navigation={this.props.navigation} saveEdit={this.saveEdit} />
render() {
return(
<View style={styles.container}>
<ScrollView style={styles.scrollContainer}>
{notes}
</ScrollView>
<Details navigation={this.props.navigation} saveEdit={this.saveEdit} />
</View>
);
}
Why?
You are using your Details component in Main screen. So you need to give navigation to Details's props from your Main to use navigation props in Details component.
Because your Details component is not the screen component registered in your navigator(router).
I tried to run your code on my machine but it seems you have too many syntax error in your code (maybe because of copy pasta?)
but it seems you should change
<TouchableOpacity onPress={this.props.saveEdit(selectedTask, this.state.dueDate)}
in Detals.js to
<TouchableOpacity onPress={this.props.navigation.getParams('saveEdit')(selectedTask, this.state.dueDate)}
for clarification this worked for me
in MainPage.js
_test(){
console.log('test');
}
.
.
.
<ActionButton
buttonColor="rgba(231,76,60,1)"
onPress={() => NavigationService.navigate('AddNewSession', {test: this._test})}>
</ActionButton>
and in AddNewSession.js
componentDidMount()
let test = this.props.navigation.getParam('test');
test();
}
There are many mistakes within your codes. First of all you are importing the navigation build-in function {createStackNavigator} in all your files, Main.js and Details.js :
import { createStackNavigator, createAppContainer } from
"react-navigation";
That make me think that you didn't know how the stack navigation or navigation in general functions in react native. You should have a file that handles your routes configuration, let call it MyNavigation.js and then define the routes 'Main' and 'details' in MyNavigations,js. It's only inside MyNavigation.js that you can import "createStackNavigator". Then you will define your functions to move between the screens "Main" and "detail". Those functions will be passed as props to the routes when moving between one another. The overall action wihtin MyNavigation.js will look like:
import React from 'react';
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import Main from './Main';
import Detail from './Detail';
const Stack = createStackNavigator();
function goToDetailFromMainScreen(){
return(this.props.navigation.navigate('switch2'));
}
function DetailSaves(){
return(//your code here to save details);
}
//Here you pass the functions to Main Component to handele Detail componets
's actions
function switch1(){
return(<Main GoToDetails={() => this.goTodetailFromMainScreen()} paramsForDetailActions={() => this.detailSaves()} />)
}
function switch2(){
return(<Details />)
}
export default function MyNavigation() {
return(
<NavigationContainer>
<Stack.Navigator initialRouteName='switch1'>
<Stack.Screen name='switch1' options={{header:()=>null}} component={Main} />
<Stack.Screen name='switch2' options={{headerTitle:"Detail"}} component={Detail} />
</Stack.Navigator>
</NavigationContainer>
)
}
Now inside Main.js you check the props functions passed to it from MyNavigation.js:
// Main.js
constructor(props){
super(props);
}
goToDetails = () => {
this.props.onPress?.();
}
paramsForDetailActions= () => {
this.props.onPress?.();
}

React Native - Is not a function - Is Undefined

I have the following code in React Native
import React from "react";
import {
StyleSheet,
Text,
View,
Button,
TextInput,
Image,
ScrollView
} from "react-native";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
apiData: [],
};
this.getButton();
}
deleteButton(Id){
fetch("http://192.168.2.22:9090/usuario/" + (Id), {
method: "DELETE"
})
.then(responseData => {
console.log(responseData.rows);
})
.done();
this.dataId = null;
}
render() {
const data = this.state.apiData;
let dataDisplay = data.map(function(jsonData) {
return (
<View style={styles.lista} key={jsonData.id}>
<View style={styles.bordeLista}>
<View style={styles.fila}>
<View style={styles.contenedorfoto}>
<Image
style={styles.foto}
source={require("./img/login.png")}
/>
</View>
<View style={styles.datos}>
<Text>Nombre: {jsonData.nombre}</Text>
<Text>E-mail: {jsonData.email}</Text>
<Text>Telefono: {jsonData.telefono}</Text>
</View>
</View>
<View style={styles.fila}>
<View style={styles.contenedorboton}>
<View style={styles.botoniz}>
<Button title="Modificar" onPress={() => {}} />
</View>
<View style={styles.botonde}>
<Button
title="Eliminar"
onPress={() => this.deleteButton(jsonData.Id)}
color="#ee4c4c"
/>
</View>
</View>
</View>
</View>
</View>
);
});
return (
<Text style={styles.titulo}>Usuarios desde BD MySQL</Text>
<ScrollView>
<View>{dataDisplay}</View>
</ScrollView>
</View>
);
}
}
And I want to call deleteButton() from this button
<Button
title="Eliminar"
onPress={() => this.deleteButton(jsonData.Id)}
color="#ee4c4c"
/>
But I get the following error, That the method is not a function and that it is not defined.
Error
How could I use the function? And I'm setting the parameter well (id). Thank you.
PS: I have deleted parts of the code and only left the most important, if you need the full code I can provide it
You're losing the reference to this because you're using an old-style lambda.
Replace this
data.map(function(jsonData) {
with an arrow function, like this
data.map(jsonData => {

FlatList inside ScrollView doesn't scroll

I've 4 FlatLists with maxHeight set to 200 inside a ScrollView.
<ScrollView>
<FlatList/>
<FlatList/>
<FlatList/>
<FlatList/>
</ScrollView>
and when I try to scroll a FlatList, it doesn't scroll but the ScrollView scrolls. How do I fix this issue ?
Full Source Code
import { Component, default as React } from 'react';
import { FlatList, ScrollView, Text } from 'react-native';
export class LabScreen extends Component<{}> {
render() {
return (
<ScrollView>
{this.renderFlatList('red')}
{this.renderFlatList('green')}
{this.renderFlatList('purple')}
{this.renderFlatList('pink')}
</ScrollView>
);
}
getRandomData = () => {
return new Array(100).fill('').map((item, index) => {
return { title: 'Title ' + (index + 1) };
});
};
renderFlatList(color: string) {
return (
<FlatList
data={this.getRandomData()}
backgroundColor={color}
maxHeight={200}
marginBottom={50}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => <Text>{item.title}</Text>}
/>
);
}
}
snack.expo link
We can use the built-in nestedscrollenabled prop for the children FlatList/ScrollView components.
<FlatList nestedScrollEnabled />
This is only required for Android (Nested scrolling is supported by default on iOS).
I was having a very similar issue until I came across an almost complete solution in a very helpful comment on one of the GitHub issues for the react-native project: https://github.com/facebook/react-native/issues/1966#issuecomment-285130701.
The issue is that the parent component is the only one registering the scroll event. The solution is to contextually decide which component should actually be handling that event based on the location of the press.
You'll need to slightly modify your structure to:
<View>
<ScrollView>
<View>
<FlatList />
</View>
<View>
<FlatList />
</View>
<View>
<FlatList />
</View>
<View>
<FlatList />
</View>
</ScrollView>
</View>;
The only thing I had to change from the GitHub comment was to use this._myScroll.contentOffset instead of this.refs.myList.scrollProperties.offset.
I've modified your fully working example in a way that allows scrolling of the inner FlatLists.
import { Component, default as React } from "react";
import { View, FlatList, ScrollView, Text } from "react-native";
export default class LabScreen extends Component<{}> {
constructor(props) {
super(props);
this.state = { enableScrollViewScroll: true };
}
render() {
return (
<View
onStartShouldSetResponderCapture={() => {
this.setState({ enableScrollViewScroll: true });
}}
>
<ScrollView
scrollEnabled={this.state.enableScrollViewScroll}
ref={(myScroll) => (this._myScroll = myScroll)}
>
{this.renderFlatList("red")}
{this.renderFlatList("green")}
{this.renderFlatList("purple")}
{this.renderFlatList("pink")}
</ScrollView>
</View>
);
}
getRandomData = () => {
return new Array(100).fill("").map((item, index) => {
return { title: "Title " + (index + 1) };
});
};
renderFlatList(color: string) {
return (
<View
onStartShouldSetResponderCapture={() => {
this.setState({ enableScrollViewScroll: false });
if (
this._myScroll.contentOffset === 0 &&
this.state.enableScrollViewScroll === false
) {
this.setState({ enableScrollViewScroll: true });
}
}}
>
<FlatList
data={this.getRandomData()}
backgroundColor={color}
maxHeight={200}
marginBottom={50}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => <Text>{item.title}</Text>}
/>
</View>
);
}
}
Hopefully you find this useful!
This is the simplest answer that requires zero configuration.. and it works like a charm
<ScrollView horizontal={false}>
<ScrollView horizontal={true}>
<Flatlist
....
....
/>
</ScrollView>
</ScrollView>
I fixed my problem with nested FlatList not being able to scroll items on android by simply importing FlatList
import { FlatList } from 'react-native-gesture-handler';
If this would not work, also try to import ScrollView.
import { ScrollView } from 'react-native';
// OR
import { ScrollView } from 'react-native-gesture-handler';
You need to play around with these imports, at least it worked in my case.
Try to set the FlatList as nested
nestedScrollEnabled={true}
Using View with a flex:1 instead of ScrollView worked for me.
Use map instead of Flatlist, same result and don't break the application
Minha conta
{
buttonsProfile.map(button => (
<ArrowButton
key={button.key}
title={button.title}
iconName={button.icon}
toogle={button.toogle}
onPress={() => {navigation.navigate(button.route)}}
/>
))
}
The better answer is to put a horizontal ScrollView inside of the other ScrollView and then the FlatList