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 }
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 use a RootForm as the basic template for form page. There is one field associated with location autocomplete, so I wrap the native autocomplete of react-native and use it in that field. However, the autocomplete dropdown list is blocked by other fields in the form which are rendered behind it. I try to search online but no useful materials. Using modal or zIndex is not the solution here. How could I make the dropdown list on the top of other components even if it renders earlier than other components?
The following two snippets are my rootform and autocomplete render functions.
render() {
const { input } = this.state;
const cities = this.state.cities;
return (
<View style={styles.container}>
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.autocompleteContainer}
data={cities}
defaultValue={input}
onChangeText={text => this.setState({ input: text })}
placeholder="Enter Location"
renderItem={({ cityName, cityId }) => (
<TouchableOpacity style={styles.listStyle} onPress={() => this.setState({ input: cityName, cities: [] })}>
<Text style={styles.itemText}>
{cityName}
</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
render() {
const data = this.props.data;
let fields = [];
let onPress = null;
Object.keys(data).forEach((key, index) => {
let options = data[key].options ?
data[key].options : null
if ("type" in data[key]) {
fields.push(
<View style={styles.formField} key={key}>
<Text style={styles.text}>{data[key].label}</Text>
<AutoComplete />
</View>
)
} else {
let custom = [styles.formField];
if (options) {
fields.push(
<View style={custom} key={key}>
<Text style={styles.text}>{data[key].label}</Text>
<TextInput value={data[key].value} style={styles.input}
readOnly
{...options} />
</View>
)
} else {
fields.push(
<View style={custom} key={key}>
<Text style={styles.text}>{data[key].label}</Text>
<TextInput value={data[key].value} style={styles.input}
onChangeText={(text) => this.props.onFieldChange(key, text)}
{...options} />
</View>
);
}
}
})
return (
<KeyboardAwareScrollView style={styles.container}>
{fields}
</KeyboardAwareScrollView>
)
}
You can just change your style.container to have a higher zIndex than whatever is appearing on top of it. However this will have the other items in the form appear behind the area reserved for the dropdown list, and render them unselectable.
If you want items underneath the Autocomplete component's area to still be interactive/selectable, you can use React.useState in order to have a dynamic zIndex property on your component.
const [componentZIndex, setComponentZIndex] = React.useState(1);
You will want your components behind the area reserved for the list to have a zIndex higher than 2 so that they are interactive.
Then you'll want to render your own input component so that you have access to the onFocus property. Luckily, the library you are using allows you to do this:
renderTextInput={() => (
<View style={styles.input}>
<TextInput
onFocus={() => setComponentZIndex(999)}
value={value}
/>
</View>
)}
This will bring the list to the top whenever the user is using the autocomplete component. The last thing to do is to make sure that you push it to the back once the user is no longer using it.
React.useEffect(() => {
Keyboard.addListener('keyboardDidHide', _keyboardDidHide);
return () => {
Keyboard.removeListener('keyboardDidHide', _keyboardDidHide);
};
}, []);
const _keyboardDidHide = () => {
setComponentZIndex(1)
};
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.