Can't use hooks in FlatList renderItem - reactjs

Please! Can somebody explain me whats wrong with following code. I'm trying to pass ListItem
export const ListItem: ListRenderItem<IUser> = ({item}) => {
return (
<RNEListItem onPress={() => {}}>
<Avatar source={{uri: item.picture.thumbnail}} rounded size="medium" />
<RNEListItem.Content>
<RNEListItem.Title>{`${item.name.first} ${item.name.last}`}</RNEListItem.Title>
<RNEListItem.Subtitle>{item.email}</RNEListItem.Subtitle>
</RNEListItem.Content>
<RNEListItem.Chevron size={30} />
</RNEListItem>
);
};
to renderItem prop in FlatList
return (
<FlatList
data={users}
renderItem={ListItem}
ItemSeparatorComponent={ListItemSeparator}
keyExtractor={keyExtractor}
showsVerticalScrollIndicator={false}
ListFooterComponent={ListLoader}
onEndReached={handleMore}
onEndReachedThreshold={0.1}
onRefresh={handleRefresh}
refreshing={isRefreshing}
/>
);
everything fine. But when I'm trying to use hooks
export const ListItem: ListRenderItem<IUser> = ({item}) => {
const {navigate} = useNavigation<RootStackParamList>();
const handlePress = useCallback(() => {
console.log(item.login.uuid);
navigate(ERootStackScreens.USER_SCREEN, {id: item.login.uuid});
}, []);
return (
<RNEListItem onPress={() => {}}>
<Avatar source={{uri: item.picture.thumbnail}} rounded size="medium" />
<RNEListItem.Content>
<RNEListItem.Title>{`${item.name.first} ${item.name.last}`}</RNEListItem.Title>
<RNEListItem.Subtitle>{item.email}</RNEListItem.Subtitle>
</RNEListItem.Content>
<RNEListItem.Chevron size={30} />
</RNEListItem>
);
};
RN return's
Hooks can only be called inside the body of a function component.
but when i change renderItem this way
return (
<FlatList
data={users}
renderItem={()=>ListItem}
ItemSeparatorComponent={ListItemSeparator}
keyExtractor={keyExtractor}
showsVerticalScrollIndicator={false}
ListFooterComponent={ListLoader}
onEndReached={handleMore}
onEndReachedThreshold={0.1}
onRefresh={handleRefresh}
refreshing={isRefreshing}
/>
);
Everything becomes fine. But it looks like types in docs are incorrect. Cuz according to them first example should work without problems
renderItem: ListRenderItem<ItemT> | null | undefined;
export type ListRenderItem<ItemT> = (info: ListRenderItemInfo<ItemT>) => React.ReactElement | null;

You need to pass the props in the functional component:
renderItem={(props)=><ListItem {...props} />}

Related

FlatList not rendering on list of text

New to react native. I have a simple component that takes in a list of ingredients and returns the items in a flatList of text. For some reason I can't get the data to render. Am I doing something wrong?
My ingredients looks like this:
const ingredients = [chicken, butter, oil]
const DisplayRec = ({ ingredients }) => {
return (
<View style={styles.container}>
<Text>Your Recipes</Text>
<FlatList
//keyExtractor={(recName) => recName}
data={ingredients}
renderItem={({ recName }) => (
<Text>{recName}</Text>
)}
/>
</View>
);
};
You are using it in incorrect manner
please try
<FlatList
//keyExtractor={(recName) => recName}
data={ingredients}
renderItem={({item, index}) => (
<Text>{item}</Text>
)}
/>
also please go throught the documentation of FlatList
https://reactnative.dev/docs/flatlist#required-renderitem
First you have to go through with the official documentation of
React-native .
flatlist documentation
you can just simply pass ingredients data to flatlist and render its
function
For live editing expo link
const ingredients = [
{
id: 'bd7',
title: 'chicken',
},
{
id: '3ac',
title: 'butter',
},
{
id: '5869',
title: 'oil',
},
];
export default function App() {
const renderItem = ({ item }) => (
<Text>{item.title}</Text>
);
return (
<View >
<FlatList
data={ingredients}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</View>
);
}
you need to use return in render item
<FlatList
//keyExtractor={(recName) => recName}
data={ingredients}
renderItem={({ recName }) => {
return (
<Text>{recName}</Text>
)}}
/>
Hope it's working fine for you

Checking for viewable items in a Flatlist and passing a prop if the item being rendered id matches a viewable items id

So basically what I said in the title, I am simply trying to change a prop that im passing to the component of Post if the item currently being rendered is in the viewport.
I am getting double the output like its firing twice, and its not even correct.
im comparing a key to the id (same thing) and if the 'activeVideo !== item.id' (video id's) are the same
the video should play, because i pass 'False' to the 'paused' property.
question is why am i getting double the output and why are both videos being paused when one of them clearly shouldnt>?
Need help fast, its for a school project.
Home.js
const [activeVideo, setActiveVideo] = useState(null);
const onViewRef = React.useRef((viewableItems)=> {
setActiveVideo(viewableItems.changed[0].key)
})
const viewConfigRef = React.useRef({ viewAreaCoveragePercentThreshold: 75 })
return (
<View>
<FlatList
data={posts}
showsVerticalScrollIndicator={false}
snapToInterval={Dimensions.get('window').height - 24}
snapToAlignment={'start'}
decelerationRate={'fast'}
onViewableItemsChanged={onViewRef.current}
viewabilityConfig={viewConfigRef.current}
renderItem={({item}) => <Post
post={item}
paused={activeVideo !== item.id}
/>}
/>
</View>
)}
Post.js
const Post = (props) => {
const [post, setPost] = useState(props.post);
const [paused, setPaused] = useState(props.paused);
console.log(props.paused)
const onPlayPausePress = () => {
setPaused(!paused);
};
const onPlayPausePress2 = () => {
setPaused(!paused);
};
return (
<View style={styles.container}>
<TouchableWithoutFeedback>
<Image
source={{uri: post.poster}}
style={styles.video2}
/>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={onPlayPausePress}>
<Video
source={post.postUrl}
style={styles.video}
onError={(e)=> console.log(e)}
resizeMode={'cover'}
repeat
paused={paused}
/>
</TouchableWithoutFeedback>
</View>
)}

React Native: What is the difference between calling props.aFunction and props.aFunction() with parenthesis?

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}
/>
);
}

Pass function requiring parameter to PureComponent properly

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.

ListHeaderComponent receives object, expected a string or class/function

I am trying to render a FlatList component with a ListHeaderComponent prop, The FlatList renders fine without that prop, but when I add ListHeaderComponent I get the following error.
Here's the render() function of the Discover class:
render() {
renderFlatListItem = (event) => {
return (
<Event
description={event.Description}
startTime={event.StartTimeToString}
Location={event.Location ? event.Location : 'TBD' }
key={event.ID}
/>
)
}
ListHeaderCreate = () => {
return (
<DiscoverSearch
resultDescription={this.state.popularEvents ? 'Popular Events':
'Search Results'}
categories={this.state.categories}
passCategory={this.handleSelectedCategory}
passInitialPosition={this.handleInitialPosition}
passLastPosition={this.handleLastPosition}
passSearch={this.handleSearch}
/>
);
}
return (
<View>
<FlatList
ListHeaderComponent={ListHeaderCreate()}
data={this.state.events}
renderItem={({ item }) => (
renderFlatListItem(item)
)}
/>
</View>
);
}
Here is the render() function of the DiscoverSearch class:
render () {
const pickerItems = this.props.categories.map((category) => {
<Picker.Item key={category.ID} label={category.Name} value={category.ID}/>
});
return (
<View>
<View>
<TextInput
style={{height: 40}}
placeholder="Search Events"
onChangeText={(text) => this.setState({searchText: text})}
/>
<TextInput
style={{height: 40}}
placeholder="Location"
onChangeText={(text) => this.setState({LocationText: text})}
/>
</View>
<View>
<Picker
onValueChange={(category) => this.props.passCategory}
>
{pickerItems}
</Picker>
<Button
title='Search'
onPress={console.log(this.state)}
/>
</View>
</View>
)
}
I assume that the VirtualizedList must be a child of the flatList that I am importing from react-native, Should I be directing this question to the react-native repo on github? I can't find where my mistake is. Any help here would be greatly appreciated.
So the issue here was with a mismatch in React Prop-Types. React Docs
The VirtualizedList component is expecting a class or a function, so passing it a function that evaluates a React Class will give it an object, and this throws an error.
The solution is to pass in the function itself like so:
ListHeaderComponent={ ListHeaderCreate }

Resources