I am working with react native and expo.cli and would like to know how I can manage to add a handle to a TouchableOpacity, I have the following component:
function MainModal (){
return(
<>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
onPress={this.TouchableOpacity}
>
<Text>Volver</Text>
</TouchableOpacity>
</View>
</>
What I want to do is make a reference to this specific TouchableOpacity when it is selected, but when I use a "this" nested in the component I get the following error: Undefined is not an object (Evaluating 'this.TouchableOpacity') I know maybe This question is a bit of a novice and has more to do with how 'this' works in javascript than with React Native itself, however I can't find a way to make a reference to the selected object so that it executes something when selected. How could I do it?
You can use React.useRef() hook to create a reference to that element so you can access then to it. The idea you have of this is not the actual this. This might refer to the global object (in not strict) and might be undefined in strict mode. If you want to use this in a component I recommend you to create a class component instead of a function one. See this in MDN to learn more of it.
Anyway, the use of ref in a functional component would be this:
function MainModal (){
const touchableReference = React.useRef()
const handleTouchableClick = () => {
console.log(touchableReference.current)
//
// Outputs HTMLDivElement...
//
}
return(
<>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
ref={touchableReference}
onPress={() => handleTouchableClick()}
>
<Text>Volver</Text>
</TouchableOpacity>
</View>
</>
)
}
One standard way of passing event handler in functional component is as follows
function MainModal (props){
const onPress1 = useCallback(()=>console.log); // created inside the componet
const {onPress2} = props; // passed from props
const {onPress3} = useSomeHook(); // from context/hook
return(
<>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
onPress={onPress1}
>
<Text>Volver</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={onPress2}
>
<Text>Volver</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={onPress3}
>
<Text>Volver</Text>
</TouchableOpacity>
</View>
</>
);
}
The behaviour of this works the same in react and react-native. React functional component is executed during the render phase and you are not suppose to work with this here, given that you do not understand when and where it is called.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
Related
I am new to react-native and I am using reanimated-bottom sheet in my project and I want to control this from other functional component called as Comment.
in my main component I am passing the reference of the sheet as
<Comment ref={ref} />
but when I receive it in my comment component it says cant find variable ref
const Comment =({ref}) => {
....
<TouchableOpacity
style={styles.panelButton}
onPress={() => ref.current.snapTo(1)}>
<Text>Cancel</Text>
</TouchableOpacity>
</View>
...
}
all I want is to close the sheet from my comment component without changing it to class component.
ref is predefined property so it won't be passed to the component.
You'd better use callback instead of passing ref.
<Component ref={ref} onPressButton={() => { ref.current.snapTo(1) }} />
const Comment =({ onPressButton }) => {
....
<TouchableOpacity
style={styles.panelButton}
onPress={onPressButton}>
<Text>Cancel</Text>
</TouchableOpacity>
....
}
I want to pass the title of a React Native Button component into a neighbouring function. I am using React Native functional components only for this application.
Here's the component. I would like to pass the title of the button pressed by the user, which will be either 'English' or 'Arabic', into the function submitLanguageSelection so that I can then save that value into useLocalStorage(), a custom hook I wrote to handle AsyncStorage, so that the next time the user uses the app, their language choice will be persisted, and they will not be shown the ChooseYourLanguageScreen again.
All help appreciated, thank you.
const ChooseYourLanguageScreen = ({ navigation }) => {
const [saveData, storedValue, errorMessage] = useLocalStorage();
const [userSelectedLanguage, setUserSelectedLanguage] = React.useState('');
const submitLanguageSelection = () => {
//TODO: receive params from onPress
//TODO: save the data locally
//TODO: navigate to welcome screen
};
return (
<View style={styles.container}>
{errorMessage ? <Text>{errorMessage}</Text> : null}
<Text style={styles.text}>This is the Choose Your Language Screen</Text>
<View style={styles.buttons}>
<View>
<Button
title={'English'}
onPress={() => submitLanguageSelection()}
/>
</View>
<View>
<Button title={'Arabic'} onPress={() => submitLanguageSelection()} />
</View>
</View>
</View>
);
};
You can simply pass it to the function
<Button title={'Arabic'} onPress={() => submitLanguageSelection('Arabic')} />
And access like below
const submitLanguageSelection = (language) => {
console.log(language);
};
Getting data from a sibling component is an anti-pattern.
The source of the knowledge of the language options is the ChooseYourLanguageScreen component (as seems from your snippet), so it should hold the list of available languages. Having that, you can just iterate through them and render the appropriate components:
<View style={styles.buttons}>
{languages.map((language) => (
<View key={language}>
<Button
title={language}
onPress={() => submitLanguageSelection(language)}
/>
</View>
))}
</View>
So let's say I have a stateless component. I want this component to
display a static title but also map through an array that's
being passed in. I only want to do it this way due to styling
issues. Is this possible or is there another way to go about this?
Thanks!
const ListTitle = ({ style, title, icon, checkedItems, onPressExpand }) => (
<View>
<TouchableOpacity onPress={onPressExpand}>
<View style={ [style, baseStyles.touchableList] }>
<Text>{title}</Text>
{icon}
</View>
</TouchableOpacity>
{
checkedItems.map((item, i) => (
<View>
<Text>{item}</Text>
</View>
))
}
</View>
)
checkedItems is the array I want to pass in. {checkedItems}.map doesn't look right but neither does the way I tried. I'm starting to think this isn't possible but wanted to know what others have to say.
I got this code
constructor(props){
super(props);
this.MyClick = this.MyClick.bind(this)
}
MyClick() {
alert('ohoho')
};
AnyRequest = () => {
if(this.state.Array.length > 0 && this.state.Loaded === true){
return this.state.Array.map(function(Data){
return(
<View>
<TouchableHighlight underlayColor="#ccc" onPress={this.MyClick}>
<Icon name="check" size={30} color="#2eb82e" />
</TouchableHighlight>
</View>
);
});
}
};
I have no idea why it doesn't work. First when I used MyClick = () => instead of binding function in the constructor it was saying undefined is not an object. As soon as I bound my function in the constructor it doesn't show error anymore but it doesn't seem like a TouchableHighlight button. Even underlayColor doesn't work.
Ok, so from the comments, we can conclude that this the TouchableHighlight is indeed clickable. It's just your syntax for onPress that is incorrect. This is a very common mistake for developers without much experience in binding and ES6 syntax. There are answers that cover this already so I won't go into depth on it. Instead, I'll point out the two common ways you can correct define onPress in React Native (there are also multiple existing answers for this but they are only partially related):
The onPress prop of TouchableHighlight expects a function to be passed to it. So whatever is between the { } needs to be a function. The first way to do this is with arrow syntax which I suspect you did incorrectly:
MyClick = () => {
alert('ohoho')
};
// ... later on ...
<TouchableHighlight underlayColor="#ccc" onPress={this.MyClick}>
<Icon name="check" size={30} color="#2eb82e" />
</TouchableHighlight>
Note the this. as it's needed to correctly reference the right variable.
The second way is to bind the method as you're doing but then actually call it in a function that you pass to onPress like so:
constructor(props){
super(props);
this.MyClick = this.MyClick.bind(this);
}
MyClick() {
alert('ohoho');
};
// ... later on ...
<TouchableHighlight
underlayColor="#ccc"
onPress={() => {
this.MyClick();
}}
>
<Icon name="check" size={30} color="#2eb82e" />
</TouchableHighlight>
Note how you are defining an arrow function inside the { } and then actually calling MyClick inside that function.
If you don't understand the difference or make many mistakes like this, then I suggest brushing up on ES6 and ES7 as that is almost a requirement for writing React Native code. There are many resources online to help with this.
Finally I got it. Problem wasn't in onPress function but in map function. As soon as I was creating new function it created a new scope which means this wasn't referring to AnyRequest function anymore. Finally ES6 arrows fixed it.
constructor(props){
super(props);
this.MyClick = this.MyClick.bind(this)
}
MyClick() {
alert('ohoho')
};
AnyRequest = () => {
if(this.state.Array.length > 0 && this.state.Loaded === true){
return this.state.Array.map((Data) => (
<View>
<TouchableHighlight underlayColor="#ccc" onPress={() =>
this.MyClick();
}}>
<Icon name="check" size={30} color="#2eb82e" />
</TouchableHighlight>
</View>
));
}
};
Here is my render function of react native. if i put listview it works. if i put touchablehighlight it works. but, if it put both it doesn't work. need help.
render: function() {
return (
/* ListView wraps ScrollView and so takes on its properties.
With that in mind you can use the ScrollView's contentContainerStyle prop to style the items.*/
<ListView
contentContainerStyle={styles.list}
dataSource={this.state.dataSource}
renderRow={this._renderRow}/>
<TouchableHighlight onPress={() => this._pressRow(rowID)} underlayColor="transparent">
</TouchableHighlight>
);
},
what is wrong here? need both components to work.
You can't have 2 tags that you can return. You should wrap it inside a <View> </View> tag. This way you can abstract multiple components that you need in the page.
render: function() {
return (
/* ListView wraps ScrollView and so takes on its properties.
With that in mind you can use the ScrollView's contentContainerStyle prop to style the items.*/
<View>
<ListView
contentContainerStyle={styles.list}
dataSource={this.state.dataSource}
renderRow={this._renderRow}/>
<TouchableHighlight onPress={() => this._pressRow(rowID)} underlayColor="transparent">
</TouchableHighlight>
</View>
);
},
Hope it helps.