I am using FlatList and in my renderItem I have a PureComponent called GridItem. I am trying to pass the function wishlistAddPresed which requires the item parameter, however when done this way, the PureComponent items are all rerendering on every state change even if "item" has not changed.
How do I fix this? Thank you!
wishlistAddPresed = (item) => console.log(item)
renderItem = ({item}) => {
return (
<GridItem
item={item}
wishlistAddPresed={this.wishlistAddPresed(item)}
/>
)
}
render () {
console.log("Render products grid");
return(
<View style={styles.container}>
<FlatList
data={this.state.data}
renderItem={this.renderItem}
keyExtractor={(item, index) => item.id}
/>
</View>
);
}
In my GridItem's render I have:
<TouchableOpacity style={styles.colButtonsG} onPress={this.props.wishlistAddPresed}>
<IconG name='playlist-add-check' size={26} color="green" style={styles.icon} />
</TouchableOpacity>
The trick is to pass both the callback function AND the parameter (which you are already doing) and then inside the nested component, pass the item to an anonymous callback. So in your case, you would need to do the following:
<FlatList
data={this.state.data}
renderItem={({ item }) => {
return <GridItem item={item} wishlistAddPressed={this.wishlistAddPressed} />
}}
keyExtractor={(item, index) => item.id}
/>
And for the GridItem which extends PureComponent...
class GridItem extends React.PureComponent {
render() {
const {item, wishlistAddPresed} = this.props;
return (
<TouchableOpacity onPress={() => wishlistAddPressed(item)}>
<IconG name="playlist-add-check" size={26} color="green" style={styles.icon} />
</TouchableOpacity>
);
}
}
Done this way, wishlistAddPressed gets called with the specific item for that row and the function equality is maintained so that the PureComponent doesn't re-render improperly like it should.
Related
I have a flatlist that is receiving a friendsArray with the following data structure...
I have a modal that has an onPress function, with that onPress i'd like to get the value of this key. I have the following Code, but usually this code provides me with the key of whatever i just selected in the flatlist... i'd like to get the key of the item selected one level deeper, how do i do this?
Here is App.js
onFriendChatPress = key => {
let friend = this.state.friendsArray.find(game => { return game.key === key })
}
render(){
return{
<View>
<FriendsModal
onChatPress={() => this.onChatPress()}
onClosePress={() => this.closeModal()}
onFriendPress={() => this.onFriendPress()}
onFriendChatPress={() => this.onFriendChatPress()}
selGame={this.state.selGame}
visible={this.state.modalVisible}
selGame={this.state.selGame}
/>
</View>
}
Here is FlatList in the FriendsModal
<FlatList
style={styles.flatList}
data={this.props.selGame.friends}
horizontal={true}
renderItem={(info) => (
<ModalProfileCard
displayName={info.item.displayName}
image={info.item.image}
onFriendPress={() => this.props.onFriendPress(info.item.key)}
onFriendChatPress={() => this.props.onFriendChatPress(info.item.key)}
/>
)
}
/>
Here is the Card in the modal
function modalProfileCard(props) {
return (
<View style={styles.container}>
<TouchableOpacity onPress={props.onFriendsPress} style={styles.friendInfo}>
<Image style={styles.profImage} source={{ uri: props.image }} />
<View style={styles.nameContainer}>
<Text style={styles.name}>{props.displayName}</Text>
</View>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={props.onFriendChatPress}>
{buttonText}
</TouchableOpacity>
</View >
)
}
You can do something like this.
const onPress = (key) => {
//you have the key here
}
return (
<Flatlist
data={your_data}
renderItem={({item})=> <YourComponent {...item} onPress={onPress} />}
/> )
const YourComponent = ({key,onPress}) => {
const onPress = () => {
onPress(key);
}
return (
<TouchableOpacity onPress={onPress}>
</TouchableOpacity>
)
}
Though I understand is
In renderItem, shouldn't you use info.displayName instead of info.item.displayNam? and so on ..
There is also a parent FlatList? You are saving the selected game in state as selGame right? You can save it's index also and can pass to child component so It would be easy to find the selected game's key.
This TouchableOpacity component: <TouchableOpacity onPress={props.goToDetails()}> takes props from its parent function, in this code, when calling the function goToDetails my code only works and shows the right results when using parenthesis, while sometimes when I just use the name of the function -in the class-, the code works fine without parenthesis: onPress={this.goToDetails}.
This is the full code,
In the class:
render(){
return (
<View>
<MovieList results ={this.state.searchResults} goToDetails={()=>this.goToDetails} />
</View>
)}
goToDetails=()=>{
this.props.navigation.navigate('Details')
}
Independent function:
const MovieList = props =>{
const renderItem = ({item}) =>(
<TouchableOpacity onPress={props.goToDetails()}>
<Image source={{uri:`http://img.omdbapi.com/?apikey=&`}} style={{height:200, width:200}} />
<Text>Title: {item.Title}</Text>
</TouchableOpacity>)
return (<FlatList style={{flex:1}} renderItem={renderItem} data={props.results} />) }
UPDATE:
The class calling MovieList code, the TouhcableOpacity code
You pass goToDetails callback inconsistently.
When you goToDetails={()=>this.goToDetails} this is a callback that returns a function, so in Movielist when it's attached to a handler as onPress={props.goToDetails()} it needs to be invoked immediately in order to get the returned function in order to again have a signature of onPress={callbackFn} or onPress={e => callbackFn(e)}.
render(){
return (
<View>
<MovieList
results={this.state.searchResults}
goToDetails={this.goToDetails} /> // Pass callback as reference
</View>
)}
goToDetails=()=>{
this.props.navigation.navigate('Details')
}
MovieList
const MovieList = ({ goToDetails }) =>{
const renderItem = ({ item }) =>(
<TouchableOpacity onPress={goToDetails}> // Attach callback to event handler
<Image
source={{ uri:`http://img.omdbapi.com/?apikey=&` }}
style={{ height:200, width:200 }}
/>
<Text>Title: {item.Title}</Text>
</TouchableOpacity>
);
return (
<FlatList
style={{ flex:1 }}
renderItem={renderItem}
data={props.results}
/>
);
}
I have this notes that I am rendering in a FlatList and I want to navigate to the item that is tapped. How can i send in with the navigation the entire item instead of doing id={id} name={name} etc, is it possible to navigate to that item and send to the view the entire item?
class MyNotes extends Component {
render() {
const { notes, navigate } = this.props;
return (
<View style={styles.view}>
<FlatList
numColons={notes.length}
data={notes}
renderItem={({ item: { id, name } }) => {
return (
<View>
<Note
name={name}
id={id}
navigate={navigate}
/>
</View>
);
}}
keyExtractor={item => item.id}
/>
</View>
);
}
}
export default Notes;
Create an onPress handler for your note item and in that you can pass your note item to your view
renderItem={({ item }) => {
return (
<View>
<Note
name={item.name}
id={item.id}
navigate={navigate}
onPress={() => navigate('Note', {...item})}
/>
</View>
);
}}
Yes, you could just pass using the spread operator. So in your case, it would be
<Note {...item} />
I am trying to create a component that is a flatlist and to use probs to pass down some information to the component in the _renderItem function:
class ArticleList extends React.PureComponent {
_renderItem = ({ item }) => (
<DummyText title={item.title} />
);
render() {
return (
<View>
<FlatList
data={articles}
renderItem={this._renderItem}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
export default ArticleList;
This is the component for the FlatList.
The DummyText Component is very simple and just for trial purposes:
class DummyText extends React.Component {
render() {
console.log(this.props);
return (
<View>
<Text>{this.props.title}</Text>
</View>
);
}
}
export default DummyText;
However, when I do this, the this.probs part appears to be undefined and I get an error saying:
TypeError: Cannot read property 'title' of undefined
I have made sure that the item has an attribute title and it works fine if the _renderItemdirectly creates <View><Text>{item.title}</Text></View>.
Solution
Fix typo.
this.probs.title
=>
this.props.title
Why
Normally, undefined message say there is no value. Thus, you able to know easily what is wrong from title's parent value. And typo is happened often including me and it will be prevent by using linter as ESLint.
Actually, you are not passing item to your _renderItem function
render() {
return (
<View>
<FlatList
data={articles}
renderItem={this._renderItem}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
You have to pass item to your _renderItem function as,
render() {
return (
<View>
<FlatList
data={articles}
renderItem={(item) => this._renderItem(item)}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
Then same as you are using,
_renderItem = ({ item }) => (
<DummyText title={item.title} />
);
I am trying to add a function to return button text based on the state inside the renderItem FlatList component.
renderButton() {
return <Text>Button</Text>;
}
<TouchableWithoutFeedback>
<TouchableOpacity>
{this.renderButton()}
</TouchableOpacity>
<TouchableWithoutFeedback>
This code return an error
function this.renderButton is not a function
Update:
<FlatList
data={list}
renderItem={this.renderRow}
/>
renderRow({item}) {
return(
<TouchableWithoutFeedback>
<TouchableOpacity>
{this.renderButton()}
</TouchableOpacity>
<TouchableWithoutFeedback>
);
}
renderButton() {
return <Text>Button</Text>;
}
You need to bind the renderRow function to be able to access this.renderButton in it.
<FlatList
data={list}
renderItem={this.renderRow.bind(this)}
/>
or
<FlatList
data={list}
renderItem={() => this.renderRow()}
/>
or
renderRow = ({item}) => {
return(
<TouchableWithoutFeedback>
<TouchableOpacity>
{this.renderButton()}
</TouchableOpacity>
<TouchableWithoutFeedback>
);
}