I've been pulling data from my rails API to render in a list of already made contacts. I've been able to render the contact data easily enough on a FlatList, but now I want to order and display the data in an alphabetical SectionList depending on the first letter of the contact name.
I have managed to get rails to give me the JSON correctly (?). But getting stuck rendering the section list. I'm not sure if I need to restructure the JSON response or if I'm missing something simple in renderSectionHeader
This is the JSON:
contacts: {
data: {
J: [
{
id: 1,
email: 'js#me.com',
password_digest: '-',
phone_number: '+1',
name: 'Jay',
circle_id: 'Jay',
bio: null,
created_at: '2020-07-17T11:05:47.479Z',
updated_at: '2020-07-17T20:22:25.821Z'
}
],
K: [
{
id: 1,
email: 'kk#me.com',
password_digest: '-',
phone_number: '+2',
name: 'Kay',
circle_id: 'Kay',
bio: null,
created_at: '2020-07-17T11:05:48.479Z',
updated_at: '2020-07-17T20:22:28.821Z'
}
]
This is the SectionList:
<SectionList style={{width: '100%', marginTop: 10 }}
sections={this.props.contacts.data}
renderItem={({item}) =>
<TouchableOpacity>
<ContactBasic item={item}/>
</TouchableOpacity>}
renderSectionHeader={({section}) => (
<Text style={styles.sectionHeader}>{section.data}</Text>)}
keyExtractor={(item, index) => index}
ListEmptyComponent={() => (
<View>
<Button title="No Contacts Found" type="clear"/>
</View>
)}
/>
This is the ContactBasic Component:
<View style={styles.contactContainer}>
<View style={styles.contact}>
<Image style={styles.image}/>
<View style={styles.contactText}>
<Text style={styles.contactName}>{props.item.name}</Text>
</View>
</View>
</View>
Your json structure is not compatible
you have an object as data not an array
data: {
J: [
{}
}
You will have to convert it to a structure like below
data: [
{title:'J',contacts:[]}
]
When section list calls the reduce function in the array it throws the error.
If you can handle this on the api level it would be easy, otherwise you will have to do this at JS level.
Related
I am trying to utilise a FlatList for my expo React Native application. I have decided to save my images in a local folder to display on the home page. However, I am getting the error message below and I really do not understand where I am going wrong.
The error message is: "invalid call at line { see *** below for the line concerned }"
const Inspiration = () => {
const handleSelectedImage = (e) => {
console.log('image selected', e);
};
return (
<>
<CreatedImageModal />
<ModalGeneratingImage />
<ExamplePrompt />
<FlatList
contentContainerStyle={{ flexGrow: 1 }}
data={EXAMPLES}
style={styles.list}
numColumns={2}
keyExtractor={(item) => item.id}
ListHeaderComponent={<Prompt />}
renderItem={({ item }) => (
<Image
source={require(item.id)} // ***
onPress={() => handleSelectedImage(item)}
transition={true}
containerStyle={styles.item}
PlaceholderContent={<ActivityIndicator />}
/>
)}
/>
</>
);
};
const styles = StyleSheet.create({
list: {
width: '100%',
backgroundColor: '#000',
},
item: {
aspectRatio: 1,
width: '100%',
flex: 1,
},
});
const EXAMPLES = [
{
id: '../assets/img-1.png',
prompt: 'A synthwave style sunset above the reflecting water of the sea, digital art'
},
{
id: '../assets/img-2.png',
prompt: 'A van Gogh style painting of an American football player'
},
{
id: '../assets/img-3.png',
prompt: '3D render of a cute tropical fish in an aquarium on a dark blue background, digital art'
},
{
id: '../assets/img-4.png',
prompt: 'A cartoon of a cat catching a mouse'
},
{
id: '../assets/img-5.png',
prompt: 'A comic book cover of a superhero wearing headphones'
},
{
id: '../assets/img-6.png',
prompt: 'A hand-drawn sailboat circled by birds on the sea at sunrise'
},
{
id: '../assets/img-7.png',
prompt: 'An expressive oil painting of a basketball player dunking, depicted as an explosion of a nebula'
},
{
id: '../assets/img-8.png',
prompt: 'A hand drawn sketch of a Porsche 911'
},
{
id: '../assets/img-9.png',
prompt: 'A sea otter with a pearl earring" by Johannes Vermeer'
},
];
export default Inspiration;
Try to change your EXAMPLES and Image component as below:
<Image
source={item.id}
onPress={() => handleSelectedImage(item)}
transition={true}
containerStyle={styles.item}
PlaceholderContent={<ActivityIndicator />}
/>
const EXAMPLES = [
{
id: require('../assets/img-1.png'),
prompt: 'A synthwave style sunset above the reflecting water of the sea, digital art'
},
...
]
You need to add require to your data instead of Image component.
I am getting the following error in my implementation of FlatList
A VirtualizedList contains a cell with more than one VirtualizedList of the same orientation as the parent list. You must pass a unique listKey prop to each sibling list.
VirtualizedList trace:
Child (vertical):
listKey: rootList-header
cellKey: rootList-header
Parent (vertical):
listKey: function listKey(item, index) {
return 'createEX' + index.toString();
}
cellKey: rootList
I am also adding relevant code. The code works when I test using expo, although the error comes up. The app crashes in TestFlight.
This is my FlatList Implementation. If I remove the MetNeedsMenu part, the code works.
<FlatList style={stylesCreate.scrollStyle}
listKey={(item, index) => {return 'createEX' + index.toString()}}
ListHeaderComponent={
<View style={stylesCreate.createForm}>
<Text style={styles.screenTitle}>CREATE NEW{"\n"}ENTRY</Text>
<Text style={stylesCreate.label}>How are you feeling now?</Text>
<Dropdown mood={newMoodBefore} setMood={setMoodBefore}/>
<Text style={stylesCreate.label}>Select the areas of life that made you feel grateful.</Text>
<MetNeedsMenu selectedNeeds={needs} setSelectedNeeds={setNeeds}/>
<InfoButton/>
<Text style={stylesCreate.label}>What are you grateful for?</Text>
<GratitudeInput value={newEntry} placeholder="Write Here..." setValue={setEntry}/>
<Text style={stylesCreate.label}>How are you feeling after doing the activity?</Text>
<Dropdown mood={newMoodAfter} setMood={setMoodAfter}/>
<CustomButton text='CREATE' onPress={addEntry}></CustomButton>
</View>
}
>
</FlatList>
This is my MetNeedsMenu implementation.
const K_OPTIONS = [
{
item: 'Physical Wellbeing:',
id: '1',
},
{
item: 'Peace & Calm',
id: '2',
},
{
item: 'Energizing Moments',
id: '3',
},
{
item: 'Engagement / Flow',
id: '4',
},
{
item: 'Connection',
id: '5',
},
{
item: 'Accomplishment',
id: '6',
},
{
item: 'Meaning / Fulfillment',
id: '7',
},
{
item: 'Others',
id: '8',
}
]
const MetNeedsMenu = ({ selectedNeeds, setSelectedNeeds }) => {
return (
<View style={{ marginBottom: 20}}>
<SelectBox
width={300}
label="Select multiple"
options={K_OPTIONS}
selectedValues={selectedNeeds}
onMultiSelect={onMultiChange()}
onTapClose={onMultiChange()}
hideInputFilter={true}
isMulti
listKey={(item, index) => {return index.toString()}}
labelStyle={{
fontSize: 15,
color: '#0060ff',
}}
containerStyle={{
backgroundColor: '#ffffff',
borderRadius: 5,
}}
arrowIconColor={'blue'} //style for dropdown arrow
optionsLabelStyle={{ //style for labels of options
color: 'black',
paddingLeft: 10,
fontSize: 15,
}}
multiOptionContainerStyle={{ //style multiple selections
backgroundColor: '#0060ff',
}}
multiListEmptyLabelStyle={{ //style for SELECT text
paddingLeft: 10,
}}
toggleIconColor={'#0060ff'} //style color of select icon
selectedItemStyle={{
paddingLeft: 10,
}}
/>
</View>
)
function onMultiChange() {
return (item) => setSelectedNeeds(xorBy(selectedNeeds, [item], 'id'))
}
}
I am guessing the problem lies with MultiNeedsMenu, but I have put listKey there with no success
The error tells you that you have a FlatList or Scrollview inside another FlatList or Scrollview with the same orientation. What is possible to do is to have nested FlatList or ScrollView of different orientation. Here is an example:
<FlatList
data={...}
keyExtracto={...}>
<ScrollView horizontal>
<Text>One</Text>
<Text>Two</Text>
</ScrollView>
</FlatList>
I have this array of data in a .js file:
export const EXPLORE_CATEGORIES = [
{
id: 1,
name: 'E-Scooters',
groups: 520,
iconName: 'Photo.png',
description: 'Small and big e-scooters',
},
…
]
And in my view, I render a FlatList with a component. This component loads an image as an icon.
return (
<View style={{ ...styles.screen }}>
<BodyOne style={styles.text}>This is the explore view.</BodyOne>
<FlatList
keyExtractor={item => item.id}
data={EXPLORE_CATEGORIES}
renderItem={itemData => (
<TwoLineWithIcon
icon={require('../../assets/images/icons/' + itemData.item.iconName)}
title={itemData.item.name}
subtitle={itemData.item.description}
caption={itemData.item.groups + ' groups'}
/>
)}
/>
</View>
);
The component has source={props.icon} so, in my FlatList's renderItem icon={...} prop I would like to chain both path and iconName but failed miserably.
this works:
icon={require('../../assets/images/icons/Photo.png'}
But I would like to do this:
icon={require('../../assets/images/icons/' + itemData.item.iconName)}
Is this possible?
Dynamic require/imports are not supported in react-native. You might want to modify your code as below
export const EXPLORE_CATEGORIES = [
{
id: 1,
name: 'E-Scooters',
groups: 520,
iconName: require('../../assets/images/icons/Photo.png'),
description: 'Small and big e-scooters',
},
…
]
return (
<View style={{ ...styles.screen }}>
<BodyOne style={styles.text}>This is the explore view.</BodyOne>
<FlatList
keyExtractor={item => item.id}
data={EXPLORE_CATEGORIES}
renderItem={itemData => (
<TwoLineWithIcon
icon={itemData.item.iconName}
title={itemData.item.name}
subtitle={itemData.item.description}
caption={itemData.item.groups + ' groups'}
/>
)}
/>
</View>
);
This is my data to render in Section List
this.state = {
data: [
{
title: "Popular Items",
data: [
{
key: 1,
name: "Briyani",
image: "https://bootdey.com/img/Content/avatar/avatar6.png",
uniqueID: 1,
cnt: 0
},
{
key: 2,
name: "Chicken Lollypop",
image: "https://bootdey.com/img/Content/avatar/avatar1.png",
uniqueID: 2,
cnt: 0
},
{
key: 3,
name: "Chicken kabab",
image: "https://bootdey.com/img/Content/avatar/avatar7.png",
uniqueID: 3,
cnt: 0
}]}]};
SectionList to Display
<SectionList
sections={this.state.data}
renderSectionHeader={({ section }) => {
return (
<View style={styles.titleContainer}>
<Text style={styles.title}>{section.title}</Text>
</View>
);
}}
keyExtractor={item => item.uniqueID.toString()}
extraData={this.state}
renderItem={({ item }) => {
return (
<View style={styles.container}>
{item.cnt === 0 ? (
<Button
title="Add"
onPress={e =>
this.incrementItem(e, item.uniqueID, item.cnt)
}
/>
) : (
<View>
<Button
title="-"
onPress={e =>
this.decrementItem(e, item.uniqueID, item.cnt)
}
/>
<Text>
{item.cnt}
</Text>
<Button
title="+"
onPress={() =>
this.incrementItem(item.uniqueID, item.cnt)
}
/>
</View>
)}
</View>
);
}}
/>
Initially the cnt = 0 based on that condition i render first button After i press i increment the count correctly based on uniqueID and re-render the component .Again when i Press on newly rendered Button it returns different uniqueID. Is there any solution to solve this. Thanks in advance.
I figured it out its because of i didn't pass event argument in incrementItem function i only pass two arguments but i didn't know why the function called if we even pass two arguments but function expects three params.
I have a prop where
console.log(this.props.userList);
returns
Array [
Object {
"userData": Object {
"userName": "David",
"userAge": 20,
},
"id": "ax3",
},
Object {
"userData": Object {
"userName": "Phillip",
"userAge": 27,
},
"id": "xq4",
},
Object {
"userData": Object {
"userName": "Kelly",
"userAge": 28,
},
"id": "pj7"
}
]
I am trying to generate a Flatlist from that data by writing:
<FlatList
data={this.props.userList}
keyExtractor={item => item.id}
renderItem={({ item }) =>
<Text>{item.userData.userName}</Text>
}
/>
When testing through Expo on my iphone, the app just freezes. Doesn't throw any errors or anything. Is this method incorrect?
*Adding in full render below
render() {
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.mainViewStyle}>
<View style={styles.searchBarViewStyle}>
<TextInput
style={styles.textInputStyle}
placeholder="User"
autoCorrect={false}
autoCapitalize="none"
onChangeText={this.onUserChanged.bind(this)}
value={this.props.userInput}
/>
</View>
<View>
<Button
title="press this"
onPress={() =>
this.props.navigation.navigate("yearSearch")
}
/>
</View>
<View>
<Button
title="testUserSearch"
onPress={() => this.showData()}
/>
</View>
<View>
<FlatList
data={this.props.userList}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<Text>{item.userData.userName}</Text>
)}
/>
</View>
</View>
</TouchableWithoutFeedback>
);
}
If I take out the Flatlist part of it, it runs fine. The showData() function currently just console logs this.props.userList, which returns the array post in the beginning.
you can flatten your array of object in one array (place all the objects in one array after receiving using some loop) and then use it in flat list (since you know how many entries each object has). to understand it further see the correct answer here.
react native flatlist with several arrays of data