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>
));
}
};
Related
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
first, sorry for my bad English. I'm coding a function and have error: Maximum update depth exceeded here is error image
This is code cause error:
I have an arrow function that will change state value after touchable onPress
toggleEdit = () => {
setTimeout(() => {
this.setState({
isEdit: !this.state.isEdit
})}, 150 )
}
This is Render function :
render() {
return (
<View style={styles.body}>
<View style={styles.headerTitle}>
<View>
<TouchableOpacity onPress={this.buttonLeft}>
<Icon
name="chevron-left"
type="evilicon"
color="#FFFFFF"
size={calcScale(62)}
/>
</TouchableOpacity>
</View>
<View>
<Text style={styles.titleFont}>
{constQuotes.constQuoteAmount.label_title}
</Text>
</View>
<View style={{marginLeft: 'auto', marginRight: calcScale(17)}}>
<TouchableOpacity onPress={this.toggleEdit}>
<Text style={styles.titleAction}>
{!this.state.isEdit ? `Edit` : `Cancel`}
</Text>
</TouchableOpacity>
</View>
</View>
</View>
);
}
When I call toggleEdit with onPress={this.toggleEdit}, function run ok,
But I call that with onPress={this.toggleEdit()}:
<View style={{marginLeft: 'auto', marginRight: calcScale(17)}}>
<TouchableOpacity onPress={this.toggleEdit()}>
<Text style={styles.titleAction}>
{!this.state.isEdit ? `Edit` : `Cancel`}
</Text>
</TouchableOpacity>
</View>
I have error Maximum update depth exceeded
What is difference between two ways ? Why did I have that error ?
Please help me, tks so much!
EDIT: Your error is because you're creating a loop with your onPress.
When I call toggleEdit with onPress={this.toggleEdit}, function run
ok, But I call that with onPress={this.toggleEdit()}:
This is a key point of how javascript works. Your first example is the correct one. It essentially says "when I press this, run the function this.toggleEdit()" The second example is "when I press this, run the function that this.toggleEdit() returns". In your particular case, toggleEdit() doesn't return a function; it causes an effect. And because everything within render() gets run any time any effect happens, the function will run forever.
As a side note: (original answer)
Remember that state updates may be asynchronous. Because you're updating state within your setState call, you need to use the function form.
toggleEdit = () => {
setTimeout(() => {
this.setState(state => {
isEdit: !state.isEdit
})}, 150 )
}
When you call this.toggleEdit() your call your function directly : at each render toggleEdit is executed, and you have a lifecycle loop, but when if you call your function without parenthesis you pass into TouchableOpacity props to your function so it's executed once. If it's not very clear I suggest you doc official documentation : https://reactnative.dev/docs/handling-touches.
replace
onPress={this.toggleEdit()}
by
onPress={()=>this.toggleEdit()}
In second case you are calling function in-place. You are not passing a function but the value returned by it and because that function is responsibe for rendering, infinite loop occurs.
In first case you are passing that function to onPress to execute it later when event happens.
I'm building a React Native app and using the tomb-form-native library for my forms. In one of my screens, I loop through an array of types and output a form for each type:
{my_types.map(ob =>
<View key={++i}>
<Text>{ob.type} #{ob.num}</Text>
<Form
ref={(c) => {
this.form = {}
this.form[ob.type] = c
}}
type={_formType(this, ob.type)}
options={_formOptions(ob.type)}
value={this.state.value}
onChange={this.onChange.bind(this)}
/>
</View>
)}
<TouchableHighlight style={styles.button} onPress={this.onPress.bind(this)}>
<Text style={styles.buttonText}>Submit</Text>
</TouchableHighlight>
But when I try to get the submitted values in my onPress function, it doesn't work for multiple types. It works for one type if I only call getValue() once:
input = this.form['my_type'].getValue()
console.log(input) // I see in my debugger that it works.
But if I try to get the input for two or more types, I don't see anything in the log...
input = this.form['my_type'].getValue()
console.log(input) // Nothing. It doesn't work.
input2 = this.form['my_other_type'].getValue()
console.log(input2) // Nothing here either.
Is it possible to use the tcomb library to submit multiple forms with one onPress? Maybe it's the way I call my onPress function in the onPress property of TouchableHighlight?
UPDATE
This simplified onPress function suggests my form ref is only working the last time through the loop. If my loop has two items...
onPress() {
let input = this.form[1]
console.log(input) // Undefined.
let input2 = this.form[2]
console.log(input2) // Object.
}
It appears to be possible. If I use an array to track the form refs, it works:
this.form = []
return (
...
{a.map(ob =>
<View key={++i} style={s}>
<Text>{ob.type} #{ob.num}</Text>
<Form
ref={(c) => {
this.form.push(c)
}}
key={i}
type={_formType(this, ob.type)}
options={_formOptions(ob.type)}
value={this.state.value}
onChange={this.onChange.bind(this)}
/>
</View>
)}
<TouchableHighlight style={styles.button} onPress={this.onPress.bind(this)}>
<Text style={styles.buttonText}>Submit</Text>
</TouchableHighlight>
And here is a simplified onPress...
onPress() {
let tF = this.form
tF.forEach(function(f) {
if (f) { // First two times through the loop, f is null, in my application.
console.log(f.getValue()) // It works!
}
})
}
<TouchableOpacity
style={{backgroundColor: '#FF00FF'}}
onPress={this.inputField}
>
<UselessTextInput
multiline = {true}
numberOfLines = {4}
onChangeText={(text) => this.setState({text})}
value={this.state.text}
/>
</TouchableOpacity>
The function onPress call is:
inputField() {
console.log('Clicked')
}
i can't find any solution y its not working
i tried changing onPress to onPress={() => { console.log('Clicked') }}
it still didn't work
Can anyone tell me y its not working what i m doing wrong in it
try passing an arrowed function to the onClick prop, otherwise it won't be executed when you click on it, there are two different ways which lead to the same result :
first (in the function declaration) :
inputField = () => {
console.log('Clicked')
}
second (in the onPress prop) :
onPress={() => this.inputField}
Try adding () to the this.inputField, like the code:
onPress={()=>{this.inputField()}}
When clicking on a button, I want a value to increment by one each time. I want to call a function incrementCounter() to handle the operation for me, rather than: onPress={() => this.setState({Count: ++this.state.Count})}
This is my code so far for the class:
class Main extends React.Component{
constructor(props){
super(props);
this.state = {
Count: 0,
}
}
incrementCounter(){
this.setState({
Count: this.state.Count + 1 //!!! Where error occurs
});
}
render() {
return(
<View style={styles.mainContainer}>
<Text style={styles.title}>{'Count: ' + this.state.Count} </Text>
<TouchableHighlight
style={styles.button}
onPress={this.incrementCounter}
underlayColor='white'>
<Text style={styles.buttonText}>+</Text>
</TouchableHighlight>
</View>
)
}
};
The code above results in an error - a red screen:
"undefined is not an object (evaluating 'this.state.Count')" with the error on the line of the incrementCounter() <- Where comments show error occurs.
Most of the examples that I can find online are not using the ES6 syntax, however I want to try and stick to this to keep it standard across my application. The future work will include having a decrementCounter() function that will do the opposite, however will not allow the counter to drop below zero.
I see you are using the es6 notation, try the following:
render() {
return(
<View style={styles.mainContainer}>
<Text style={styles.title}>{'Count: ' + this.state.Count} </Text>
<TouchableHighlight
style={styles.button}
onPress={this.incrementCounter.bind(this)}
underlayColor='white'>
<Text style={styles.buttonText}>+</Text>
</TouchableHighlight>
</View>
)
}
Notice the .bind(this) on the event, this is needed so that it acutally references the class itself not some global function.