FlatList's renderItem doesn't recognise "this" keyword - react-native

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.

Related

useEffect not working in custom drawer component without refresh

So I am using react-navigation 5 and I have a custom drawer component for my app. I want to display the name of the logged-in user in the drawer for which I am using a state variable and I am updating the state from firestore. I am calling a function in useEffect which accesses firestore and gets the name of the user. But I think the useEffect is not working without refresh because unless I save the project and refresh the application the state is not getting updated in the application and I cannot see the name of the user without refreshing but it is visible after a refresh. Any ideas why this is happening? Any help would be appreciated. Thank you.
Custom drawer
export default function CustomDrawer(props) {
const paperTheme = useTheme();
const [name,setName]=useState('');
useEffect(() => {
doStuff();
}, []);
const doStuff = async () => {
var phone=global.phone;
await firestore().collection("Users").where('Phone Number', '==', phone).get().then(querySnapshot=>{
querySnapshot.forEach(documentSnapshot => {
console.log("in drawer");
console.log(documentSnapshot.data());
setName(documentSnapshot.data().Name);
})
})
};
return(
<View style={{flex:1}}>
<DrawerContentScrollView {...props}>
<View style={styles.drawerContent}>
<View style={styles.userInfoSection}>
<View style={{flexDirection:'row',marginTop: 15}}>
<Avatar.Image
source={{
uri: ''
}}
size={50}
/>
<View style={{marginLeft:15, flexDirection:'column'}}>
<Title style={styles.title}>{name}</Title>
</View>
</View>
</View>
</View>
</DrawerContentScrollView>
</View>
);
}
Looks like you have doStuff function defined outside the useEffects.
Either you need to put it inside useEffects or add it in dependency list
useEffect(() => {
doStuff();
}, [doStuff]);

How to prevent flatlist header or footer from re-render in reactnative

I put an input field in the footer of flatlist but when i try to type anything it dismiss the keyboard automatically because of the re-render of the flatlist footer..
I tried to nest the flatlist from Scrollview but this brings warning..
How can i stop the footer from being re-rendered? can i fix this without nest the flatlist from Scrollview?
<FlatList
ListHeaderComponent={() => (
<View style={styles.discountContainer}>
<Text style={[styles.buttonText, { letterSpacing: 3 }]}>
10% DISCOUNT ON 8 COURSES
</Text>
</View>
)}
numColumns={2}
data={data}
renderItem={({ item }) => (
<View>
<SingleProduct item={item} />
</View>
)}
ListFooterComponent={() => (
<View>
<View style={styles.couponContainer}>
<Input
placeholder='Coupon code'
placeholderTextColor='#0a5796'
color='#0a5796'
inputStyle={{
color: '#0a5796',
}}
inputContainerStyle={{
borderBottomWidth: 0,
height: 50,
}}
containerStyle={styles.couponInputContainer}
onChangeText={(value) =>
this.setState({ couponCode: value })
}
value={this.state.couponCode}
/>
{couponLoading ? (
<View style={styles.couponButton}>
<ActivityIndicator />
</View>
) : (
<TouchableOpacity
style={styles.couponButton}
onPress={() => this.codeCheck(couponCode, line_items)}
>
<Text style={styles.buttonText}>Apply Coupon</Text>
</TouchableOpacity>
)}
</View>
</View>
)}
/>
Arrow-Funktions are "always" executed and create a new Reference in Memory. This way they will always re-rendering if component will be executed.
For performance reasons you better define your function outside and call it like this:
function renderMyItem(){ ...bimbom... yous stuff goes here! }
function renderHeader(){ ...bimbom... yous stuff goes here! }
<Flatlist
renderItem={this.renderMyItem()}
ListHeaderComponent={this.renderHeader()}
...
/>
What happens here?
Both of your functions renderMyItem and renderHeader will be executed once if your component is loaded and will be saved in memory. So every time you call one of the functions, you call a reference to the place in memory where they are saved.
In the other case, Arrow-Functions ()=>{...} are executed in current context and generate a new reference in Memory, each time they called, because .. to say it clear: you define & call a function that way.
If you are using Functional Component then don't use Arrow function () => (...) for header and footer components of FlatList but instead only return your header and footer Components as shown in the sample below.
<FlatList
...
ListHeaderComponent={(<View><Text>Header</Text></View>)}
ListFooterComponent={(<View><Text>Footer<Text></View>)}
/>
I was going through the same problem and the accepted answer didn't worked for me. As here the problem occurs because we are updating the state whenever the text changes (as defined in onChangeText) which causes re-rendering. Thus i came up with another solution;
First i created another dict object newState which has nothing to do with state or props. So on changing newState dict, it will not cause re-rendering. Then;
newState = {}
<TextInput onChangeText={text => this.newState.value = text} />
Then i changed the state(which is necessary as per your problem and as per mine) on onEndEditing ;
<TextInput onChangeText={text => this.newState.value = text} onEndEditing={this.setSearch} />
Here is setSearch
setSearch= () => {
this.setState({couponCode: this.newState.value})
delete this.newState.value;
}
I am deleting the key after the state is set because it doesnot update correctly next time.

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 - Function doesn't seem to be called

I have a simple app with 2 images. When I click on image1, I want to display hello on the console.
But my function doesn't write anything in the console.
I didn't use a function and called directly console.log in the 2nd image and it works.
Do you know what is wrong with my function?
(I also used makeALogHello.bind(this) but it doesn't change the behavior).
export default class App extends React.Component {
constructor(){
super();
}
makeALogHello(){
console.log("hello");
}
render() {
return (
<View>
<TouchableOpacity style ={{flex:1}}
onPress={() => {this.makeALogHello} }>
<Image style ={styles.container}
resizeMode="contain"
source={require("./images/image1.png")}/>
<Text>ImageNb1</Text>
</TouchableOpacity>
<TouchableOpacity style ={{flex:1}}
onPress={() => {console.log("hi")} }>
<Image style ={styles.container}
resizeMode="contain"
source={require("./images/image2.png")}/>
<Text>ImageNb2</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
You need to call the function in your arrow function:
onPress={() => {this.makeALogHello()} }
Or simply pass the reference to your function, without wrapping it:
onPress={this.makeALogHello}

Call this.props in function outside render gives undefine

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= () => {}