How to handle onPress function react - javascript

I am using react native with NativeBase components, and I am having a problem calling my _doStuff function. I tried to call
onPress={this._doStuff.bind(this)}
but keep receiving
Cannot read properly 'doStuff' of undefined
_doStuff(){
console.log('Hi');
}
_getList() {
return this.state.listData.map(function(data, i){
return(
<View key={i}>
<ListItem style={styles.listItemContain}>
<Button transparent onPress={this._doStuff.bind(this)}>
<View>
<Thumbnail source={{uri: data.icon}} style={styles.thumbnailStyle}/>
<Text style={styles.title}>{data.name}</Text>
<Text note style={styles.text}>{data.vicinity}</Text>
</View>
</Button>
</ListItem>
</View>
);
});

this context is lost inside the .map loop. if you are using ES6, try to use fat arrow functions as shown below.
_getList() {
return this.state.listData.map((data, i) => {
return(
<View key={i}>
<ListItem style={styles.listItemContain}>
<Button transparent onPress={this._doStuff.bind(this)}>
<View>
<Thumbnail source={{uri: data.icon}} style={styles.thumbnailStyle}/>
<Text style={styles.title}>{data.name}</Text>
<Text note style={styles.text}>{data.vicinity}</Text>
</View>
</Button>
</ListItem>
</View>
);
});
if you cant use ES6 on your project due to various reasons use alternative approach(old school) as shown below...
_getList() {
return this.state.listData.map(function(data, i){
var that = this;
return(
<View key={i}>
<ListItem style={styles.listItemContain}>
<Button transparent onPress={that._doStuff.bind(that)}>
<View>
<Thumbnail source={{uri: data.icon}} style={styles.thumbnailStyle}/>
<Text style={styles.title}>{data.name}</Text>
<Text note style={styles.text}>{data.vicinity}</Text>
</View>
</Button>
</ListItem>
</View>
);
});

I would make a wager that the context of your _getList function does not have the context of the React Component that you are thinking. If you are using ES6 classes and would like to get autobinding of the components context I would suggest using autobind-decorator. If you decide to go that route here is a nice tutorial on setting it up.
Otherwise make sure that when you call _getList make sure to call it with either .call or .bind.
For example:
// if you are calling the function directly
this._getList().call(this)
or
// if you are passing the function off for another function to execute it
this._getList.bind(this)

Related

Same Function is Triggered IN Multiple Components

I'm using react-native-material-menu's popup for showing menu options.
But the issue is, it's not working for multiple scenarios.
I mean when I click on first menu button, the same methods gets triggered and hence the same menu is opened every time.
What should be the better approach for to handle this particular scenario.
Here is the Snack
_menu = null;
setMenuRef = ref => {
this._menu = ref;
};
hideMenu = () => {
this._menu.hide();
};
showMenu = () => {
this._menu.show();
};
{this.state.clientsList.map((item) => {
return (
<View style={styles.caseItem} >
<Card style={styles.card}>
<CardItem>
<Body>
<View style={styles.rowTitle}>
<Text style={styles.title}>{item.FullName}</Text>
<Menu
ref={this.setMenuRef}
button={<Icon type="Feather" name="more-vertical" onPress={this.showMenu} style={{ fontSize: 20, color: '#555' }} />}
>
<MenuItem onPress={this.hideMenu}>View</MenuItem>
<MenuItem onPress={this.hideMenu}>Edit</MenuItem>
<MenuItem onPress={this.hideMenu}>Delete </MenuItem>
</Menu>
</View>
<View>
<Text style={styles.lbl}>Email: <Text style={styles.lblValue}>{item.EmailID}</Text></Text>
<Text style={styles.lbl}>Client Type: <Text style={styles.lblValue}>{item.ClientType}</Text></Text>
</View>
</Body>
</CardItem>
</Card>
</View>
);
})}
you are using same reference for all menu components
you have to use different ref for every Menu.
Best Approach:
first way to Create HOC for Menu and handle them in it.
Moderate
or Create Dynamic Ref in React for Menu's List
for running code only
3rd is to create ref for every menu
_menu1 = null;
_menu2 = null;
_menu3 = null;
You are calling the same ref every-time. I've not used the library you mentioned but if it has to rely on ref you can create a list of reference instead, syntax is in this post.

Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of 'search'

The search is perfect and it's all looks fine, but gets this warning.
I get this warning when I press any key to start the search method.
"Song_ID", "Song_Name" and "Image" are of the variable names from the SQL Database.
- I looked on other question as this, but it didn't help me at all.
This is the code where the error is :
return (
<View>
<ScrollView>
{musicList.map(songObj => {
return (
<View style={styles.resultsContainer}> /// Its written that the erorr in this line
<TouchableOpacity onPress={this.GetListViewItem.bind(this, songObj.Song_Name)}>
<Text style={{ fontSize: 16 }} key={songObj.Song_ID}>
{songObj.Song_Name}</Text>
<Image source={{ uri: songObj.Image }} style={styles.img} />
</TouchableOpacity>
</View>
);
})}
</ScrollView>
</View>
);
}
I don't understand where to put the key and/or what does it mean, I tried so many times but it didn't went well.
If more details is needed please tell me and I'll insert the right code.
You should add the key onto the outermost component inside the map. In your case, this is the View. Try this:
return (
<View>
<ScrollView>
{musicList.map(songObj => {
return (
<View style={styles.resultsContainer} key={songObj.Song_ID}>
<TouchableOpacity onPress={this.GetListViewItem.bind(this, songObj.Song_Name)}>
<Text style={{ fontSize: 16 }}>
{songObj.Song_Name}</Text>
<Image source={{ uri: songObj.Image }} style={styles.img} />
</TouchableOpacity>
</View>
);
})}
</ScrollView>
</View>
);
}
Adding the key prop lets react optimize rendering which is why it recommends you add it. Read the docs for more info.

Use same onPress handle with multiple TouchableOpacity

I am using a GridView to show multiple products and on click i want to go to details screen , however I am unable to find a way to identify which product grid view cell was selected / tapped by user , I think sending some parameter to handle method should do the trick.
return (
<GridView
itemDimension={130}
items={this.state.dataSource}
style={styles.gridView}
renderItem={item => (
<View style={styles.itemContainer}>
<TouchableOpacity onPress={ this.onPressStone } style={styles.itemContainer}>
<Image source = {{uri:item.url}} style={styles.imageView} />
<Text style={styles.itemName}>{item.name}</Text>
</TouchableOpacity>
</View>
)}
/>
);
You can send parameters to your function like you said.
Example
onPress={ () => this.onPressStone(item.id) }
You can pass to the method onPressStone the item and depending the item you could do that print what you want. And if you want to know who was pressed you just have to make a log with the name of the item or take the index for example
return (
<GridView
itemDimension={130}
items={this.state.dataSource}
style={styles.gridView}
renderItem={(item, index) => {
//to know who was pressed:
console.log('pressedItemName-->', item.name);
//to know index pressed:
console.log('pressedItemName-->', item);
return (
<View style={styles.itemContainer}>
<TouchableOpacity onPress={ ()=> {this.onPressStone(item)} } style={styles.itemContainer}>
<Image source = {{uri:item.url}} style={styles.imageView} />
<Text style={styles.itemName}>{item.name}</Text>
</TouchableOpacity>
</View>
)}
/>
);

React-native: Pass event to other object

This is my first experience with JS/React/React Native. I don't yet fully grasp mechanics of this framework, so be kind to me.
What I have :
<View style={styles.container}>
<View style={styles.timerContainer}>
<View style={styles.timerBar}>
<Image style={styles.icon} source={require('../../img/time50.png')}/>
{Timer} //First
</View>
<View style={styles.timerBar}>
<Image style={styles.icon} source={require('../../img/break50.png')}/>
{Timer} //Second
</View>
</View>
<View style={styles.buttonContainer}>
<TouchableHighlight onPress={this.startWork}>
<Image style={styles.imageButton} source={require('../../img/start.png')}/>
</TouchableHighlight>
<TouchableHighlight onPress={this.startBreak}>
<Image style={styles.imageButton} source={require('../../img/break.png')}/>
</TouchableHighlight>
<TouchableHighlight onPress={this.stopWork}>
<Image style={styles.imageButton} source={require('../../img/end.png')}/>
</TouchableHighlight>
</View>
</View>
Timer is a custom class, which will have 'start', 'pause' and 'end' methods. I want to call methods of both Timers when TouchableHighlight is pressed.
Overall it would look like this :
when first TouchableHighlight is pressed (startWork), Timer2.end and Timer1.start should be called, when second is pressed (startBreak), Timer1.pause, Timer2.start should be called, for third (stopWork) it would be Timer1.end, Timer2.end.
But how am I supposed to refer to these timers from onPress methods? Should I keep both Timers in some vars of parent class? I am totally stuck and have no idea where to start (and what's acceptable). Such problem is not easily googlable (or I don't know where to look). I would appreciate any help (solution, pointing to tutorial...).
When you render the timer components in this component you should assign a ref to each one of them, like
<Timer ref={r => this.timer1 = r}/>
Then in the Touchable callback functions you can call this.timer1.start()
Note: Cleaner ref setting would be to actually have another function, so you aren't constantly binding in the render function:
setTimerOne = (r) => {
this.timer1 = r;
}
render() {
<Timer ref={this.setTimerOne}/>
}

React Native : set id to a component and get this component's id

I am developing my first React Native app and I am trying to set an id to a component so that I can determine this view when pressing on it and displaying data according to the component's id
This is what I tried so far:
renderRow(rowData, sectionID, rowID){
var past_matches_circles =[];
for(var i=0; i<isPast; i++){
past_matches_circles.push(
<TouchableOpacity id={i} key = {i} collapsable={false} style={styles.small_circle} onPress={()=>this.pressOnCircle(rowData[`${i}`].MatchID)}>
</TouchableOpacity>
);
}
return (
<View>
<View style={{flexDirection:'row'}}>
{past_matches_circles}
</View>
<View style={{flexDirection:'row'}}>
<Image style={{width:100, height:100, margin:5}} source={{uri:'http://facebook.github.io/react/img/logo_og.png'}}/>
<View style={{justifyContent:'center', flex:1}}>
<Text> {rowData[0].MatchDate} </Text>
<Text> {rowData[0].HomeTeam} VS {rowData[0].AwayTeam} </Text>
</View>
</View>
</View>
);
}
pressOnCircle(i){
ToastAndroid.show('key ' + i, ToastAndroid.SHORT);
}
but unfortunately the iterator i value is always equal to its last value inside the loop regardless which view I am pressing on.
Can anyone help me getting component's id?
After several days, finally I solved this problem. And I'll post it for who will come to this question.
I was pushing the views into my array without returning so the value of i is always the same.
so that what I did:
renderCircle(id) {
return <TouchableOpacity key={id} style={styles.small_circle} onPress={() => this.pressOnCircle(id)}>
</TouchableOpacity>;
}
renderRow(rowData, sectionID, rowID){
var past_matches_circles =[];
for(var i=0; i<isPast; i++){
past_matches_circles.push(
this.renderCircle(i)
);
}
var { page, animationsAreEnabled } = this.state;
return (
<View>
<View style={{flexDirection:'row'}}>
{past_matches_circles}
</View>
<View style={{flexDirection:'row'}}>
<Image style={{width:100, height:100, margin:5}} source={{uri:'http://facebook.github.io/react/img/logo_og.png'}}/>
<View style={{justifyContent:'center', flex:1}}>
<Text> {rowData[0].MatchDate} </Text>
<Text> {rowData[0].HomeTeam} VS {rowData[0].AwayTeam} </Text>
</View>
</View>
</View>
);
}
Hope that will help.
You can use .bind to create a new function that closes over the value of i. This isn't the best for performance as you're creating a new function each time.
onPress={ this.pressOnCircle.bind(this, i) }>
Also, you might need to do something like
this.pressOnCircle = this.pressOnCircle.bind(this);
In the constructor to get the proper this context in the handler.

Categories

Resources