Call this.props in function outside render gives undefine - react-native

Im having this problem with my flatlist. Basically im destructing my code to make it more readable. However my problem whenever I call the this.props outside the render it gives my undefine. Here's my code below:
renderItem(item) {
console.log(this.props) // Gives me undefine
return (
<ListItem button>
<Text>{item.item.name }</Text>
</ListItem>
)
}
render() {
return (
<FlatList
keyboardShouldPersistTaps={'always'}
data={countries.payload.data}
renderItem={this.renderItem}
keyExtractor={item => item.name}
/>
)
}
But if I did not separate to other function the renderItem. I can access the this.props:
render() {
return (
<FlatList
keyboardShouldPersistTaps={'always'}
data={countries.payload.data}
renderItem={({item, index}) => (
<ListItem button onPress={() => this.props.onPressAction()}> // I get the correct value
<Text>{item.name }</Text>
</ListItem>
)}
keyExtractor={item => item.name}
/>
)
}

When using react-native and ES6 classes it won't automatically bind your functions that is declared on your classes, so this will be a live object represent for the unknown context or even undefined which caused this.props to be undefined.
We need to handle the correct value of this to our methods on our own. Therefore either use:
Change to this.renderItem.bind(this)
Or use arrow functions like renderItem= () => {}

Related

FlatList's renderItem doesn't recognise "this" keyword

So, I recently started making FlatList a recurring thing in the app I'm working on. I am right now working on a screen that gives a list of requests and is updated once one is accepted, which is done by pressing a button. There's a method called getNewRequests I am using to update the requests, but it can't seem to be called by the flatline, as it only returns the error TypeError: _this3 is undefined.
I really need that method to work, because I need to update the state of that screen, and trying to type the whole method there only returns the same error. In that context, this always returns undefined.
render(){
return(
<View style={GenericStyles.styles.genericContainer}>
<Text> REQUEST SCREEN </Text>
<FlatList
data={this.state.requestList}
renderItem={this.renderItem}
keyExtractor={item => item.id}
/>
<Button title="Voltar" color="cyan" onPress={() => this.props.navigation.goBack()}/>
</View>
);
}
renderItem({item}){
return(
<Card
containerStyle={{flex: 1, width: 200}}
title={item.Username}>
<Button color="blue" title="Accept" onPress={() => RequestService.allowRequest(item.id, (response) => {
let rsp = JSON.parse(response);
if(rsp.success){
this.getNewRequests();
}
})}/>
</Card>
);
}
You need to either bind the function in your constructor (or wherever you want) doing:
constructor(props){
super(props)
this.renderItem.bind(this)
}
or use arrow function:
renderItem = ({item}) => {
//your function
}
Doing this will give the function access to the this of the current component.

How can I set data in a state in FlatList?

I want to save product ID (item.id) in a state like productId.
I need to product Id for add product to cart.
When I click on TouchableOpacity working fine but productId always is 4.
I have three Item. The id of last item is 4 and first item is 2.
When I click on TouchableOpacity of product 1, id is 4 but should be 2.
I see IDs are OK When I print IDs in listView.
<FlatList
data={this.state.dataSource}
renderItem={({item}) =>
<View>
<View>
<Text>{item.title} - {item.type}</Text>
<Text>{item.id}</Text>
<TouchableOpacity onPress={this.decrementCount,()=>this.setState({productId:item.id})}>
<AntDesign name="minus" size={15} style={{color:'#fff'}}/>
</TouchableOpacity>
<TouchableOpacity onPress={this.incrementCount} activeOpacity={0.5}>
<AntDesign name="plus" size={15} style={{color:'#fff'}}/>
</TouchableOpacity>
</View>
</View>
}
/>
I think this line is your issue:
<TouchableOpacity onPress={this.decrementCount,()=>this.setState({productId:item.id})}>
First things first, you should define your render function on your class, and you should define your onPress function separate from that. For example:
class MyClass extends React.Component {
handlePress = (item) => {
this.decrementCount()
this.setState({productId: item.id})
}
renderItem = ({item, index}) => {
return (...code from above, but using this.handlePress rather than your onPress function as currently defined)
}
render() {
return (
<FlatList
data={this.state.dataSource}
renderItem={this.renderItem}
/>
)
}
}
I think the real reason that its not working for you right now is that you're trying to call this.setState as a callback in your onPress function. It would probably work as is if you changed your current onPress from:
onPress={this.decrementCount,()=>this.setState({productId:item.id})}
to
onPress={() => this.setState({productId:item.id}, this.decrementCount)}
as I'm fairly sure you can have a function called as a callback from this.setState.
Hope this helps!

React Native: Lodash Map

I'm new in react native and I want make data function into view.
my function looks like this
renderTest = () => {
<FlatList
onEndReached={0}
onEndReached={() => this.handleEnd()}
>
{_.map(this.state.leads, (leads, index) => {
return (
<Text key={index}>{leads.full_name}</Text>
)
})}
</FlatList>
}
and my View to pass the value of the function
<View style={{flexDirection: 'row'}}>
{this.renderTest()}
</View>
I don't have any idea what's the problem all I just want is to render the value. I hope could someone help me.
edited
Since you already are in react world you can simply use the Array.map:
renderTest = () => {
<FlatList onEndReached={() => this.handleEnd()}>
{this.state.leads.map((lead, index) => {
return (<Text key={index}>{lead.full_name}</Text>)
})}
</FlatList>
}
As long as this.state.leads is an array.
But bottom line is no lodash is needed here.
As seen by your comment on Akrion's answer, I'm guessing you haven't defined the renderTest function inside the component. Another possibility is that you are using a stateless component in which case you cannot access this
I'm just not sure you can define a FlatList like that one. I mean, I think you're missing the return of the function and you have to pass the property data to FlatList this way:
renderTest = () => {
return (
<FlatList
onEndReached={0}
onEndReached={() => this.handleEnd()}
data={this.state.leads}
renderItem={(lead, index) => <Text key={index}>{lead.full_name}</Text>}
/>
)
}
Let me know if this can solve your issue :)

How do I render different data types from a fetched json?

I was using...
<FlatList
data={this.state.dataSource}
renderItem={({item}) => <Text>{item.username} {item.name}</Text>}
keyExtractor={({id}, index) => id}
/>
...to render two texts in React Native but now I've got an uri in my json...
How do I render the image in an IM style (thumbnail, username and name)?
Add a renderItem method and then put everything you need in there. Once you make the renderItem method you can throw it into your FlatList.
renderItem({ item }) {
return (
<View>
<Image
source={{uri: item.uri}}
/>
<Text>
{item.username}
</Text>
<Text>
{item.name}
</Text>
</View>
);
}
Then use the method inside the FlatList (the .bind makes sure it stays in the right context)
<FlatList
data={this.state.dataSource}
renderItem={this.renderItem.bind(this)}
keyExtractor={({id}, index) => id}
/>
You can build a component that takes your fields as its props, containing multiple Texts and an Image using your uri as the source. Style this like you would any other component, and then pass that component into renderItem.
You can embed the component immediately to the renderItem like this if the flatlist item is not complicated ( has a lot of component )
<FlatList
data={this.state.dataSource}
renderItem={({item}) => {
<View>
<Image source={{uri: item.uri.image}}/>
<Text>{item.userName}</Text>
<Text>{item.name}</Text>
</View>
}}
keyExtractor={(i) => i.toString()}
/>

React Native: Conditional props for List

I was wondering if there is a way to set a group of properties for a list component based on a condition.
render() {
return (
<SectionList
ref={(ref) => {this.listRef = ref}}
style={styles.listContainer}
sections={this.props.listData}
fetchData={this.props.fetchData}
keyExtractor={this.keyExtractor}
renderSectionHeader={this.renderSectionHeader}
renderItem={this.renderItem}
ItemSeparatorComponent={this.separator}
keyboardDismissMode='on-drag'
initialNumToRender={6}
stickySectionHeadersEnabled={false}
// ListFooterComponent={this.renderFooter}
// onEndReachedThreshold={0.5}
// onEndReached={() => {
// this.props.fetchMoreData()}}
/>
)
}
I would like ListFooterComponent, onEndReachedThreshold and onEndReached only to be set if this.props.fetchMoreData is set. Do I need to put an if-statement at the top of the render function or is it possible to create a separate prop to group those 3 and use the spread operator to put this back into the SectionList based on the condition. I am not quite sure how to do this.
I ended up with the following
render() {
optional = {}
if (this.props.fetchMoreData) {
optional.onEndReachedThreshold = 0.5
optional.onEndReached = () => {
this.props.fetchMoreData()}
optional.ListFooterComponent = this.renderFooter
}
return (
<SectionList
ref={(ref) => {this.listRef = ref}}
style={styles.listContainer}
sections={this.props.listData}
fetchData={this.props.fetchData}
keyExtractor={this.keyExtractor}
renderSectionHeader={this.renderSectionHeader}
renderItem={this.renderItem}
ItemSeparatorComponent={this.separator}
keyboardDismissMode='on-drag'
initialNumToRender={6}
stickySectionHeadersEnabled={false}
{...optional}
/>
)
I think I initially tripped over optional.ListFooterComponent. It just looked strange to me so I assumed it must be wrong ;)