I'm trying to render multiple components via FlatList but i get this error : "Warning: Each child in a list should have a unique "key" prop."
I made sure the keyExtractor property was set up correctly but i believe my issue happened when trying to render multiple components inside my FlatList with map.
My FlatList and custom component looks like so :
const ItemView = (row: any) => {
let title = row.item.title
let split = title.split(search)
let searchStringCount = split.length
return (
<Text>
{
split.map((elem: string, index: number) => {
return (
<>
<Text style={styles.textListStyle}>
{elem}
</Text>
{index + 1 < searchStringCount &&
<Text style={styles.textHighLightStyle}>
{search}
</Text>}
</>
)
})
}
</Text>)
}
<FlatList
style={styles.itemStyle}
data={filteredData}
keyExtractor={(item, index) => {
return index.toString();
}}
ItemSeparatorComponent={ItemSeparatorView}
renderItem={ItemView} />
I've tried in vain inserting manually a key property to the generated Text components.
you need to add key prop when render with map
split.map((elem: string, index: number) => {
return (
<View key={index}>
<Text style={styles.textListStyle}>
{elem}
</Text>
{index + 1 < searchStringCount &&
<Text style={styles.textHighLightStyle}>
{search}
</Text>}
</View>
)
})
Related
I have two different components "HomePage" & "ListItemCA"
In HomePage, I have a FlatList and a modal popup
<FlatList
data={ listData}
keyExtractor={list => list.Title}
renderItem={({ item }) => <ListItemCA data={item} onLongPress={openModal} />}
/>
and each list item is called from another component ListItemCA
function ListItemCA({data, onLongPress}) {
return (
<TouchableOpacity onLongPress={onLongPress} >
<View style={styles.container}>
<Text style={styles.title}>{data.Title}</Text>
<View style={styles.metaContainer}>
<Text style={styles.meta}>{( data.totalMonths != null ? data.totalMonths : '0' )} Months</Text>
<Text style={styles.meta}>{( data.members != null ? data.members.length : '0' )} Members</Text>
</View>
</View>
</TouchableOpacity>
);
}
What I want to acheive?
I want to get the selected list item title on my HomePage component. When a user longpress on a list item that title should be displayed on a modal popup. How do I pass the selected list item title to the HomePage component using longpress?
If your goal is to display data from the long pressed item in the modal, you could add the data as a parameter of your openModal function:
function openModal(data) {
// your function
return (
<Text>{data.Title}</Text>
)
}
Then, in your FlatList, modify the props of ListItemCA to call openModal for the selected item:
renderItem={({ item }) => <ListItemCA data={item} onLongPress={openModal(item)} />}
If you also want to save the data from the long pressed item in your HomePage component for other uses, you could save it in the state. In your HomePage component:
import React, { useState } from 'react'
function HomePage() {
const [itemData, setItemData] = useState()
// your code
}
Then, in your flatlist:
<FlatList
data={listData}
keyExtractor={list => list.Title}
renderItem={({ item }) =>
<ListItemCA
data={item}
onLongPress={ () => {
setItemData(item)
openModal(item)
}}
/>
}
/>
You can achieve this by passing(return) parameter from your component like this -
function ListItemCA({data, onLongPress}) {
return (
<TouchableOpacity onLongPress={() => {
onLongPress(data.Title);
//return data.Title when onLongPressed clicked
}}>
<View style={styles.container}>
...
</View>
</TouchableOpacity>
);
}
then get it in props -
<FlatList
data={listData}
keyExtractor={list => list.Title}
renderItem={({ item }) =>
<ListItemCA
data={item}
onLongPress={(title) => {//this **title** return from **onLongPress(data.Title)**
openModal();
setTitle(title);// or directly can pass that title in openModal func.
}}
/>
}
/>
I am a newbie in RN and recently started using redux. I have a api response which is of the below format:
{
records : [
{
name : "cde"
groups :[
{
id : "212"
fields[{
data : "abc"
}]
}]
}
]
}
So, Inside records , I have an array of objects "groups" and inside "groups" I have array of objects "fields" and inside fields, I have data which I want to display inside FlatList. I am able to display "name" which is inside records inside FlatList As of now.
My File PeopleList.js looks like below :
export default class PeopleList extends Component {
_keyExtractor = item => name;
_renderItem = ({ item }) => {
const { name} = item;
const groups = this.props.people.map((items, index)=>{
return( <Text>{name}({items.id})</Text>)
})
//const {} = item.groups;
return (
<View>
<View style={styles.cardContainerStyle}>
<View style={{ paddingRight: 5 }}>
<Text style={styles.cardTextStyle}>
{/* {name} {"\n"} */}
{groups}
</Text>
</View>
<Image
style={styles.faceImageStyle}
// source={{ uri: picture.medium }}
/>
</View>
</View>
);
};
render() {
return (
<FlatList
style={{ flex: 1 }}
data={this.props.people}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
/>
);
}
}
PeopleList.propTypes = {
people: PropTypes.array
};
people is an array that contains the records object : responseJson.records
So, how can I display data and also what is the use of keyextractor?
As per what I have searched so far is that we need to use map function for arrays but not quite sure how to use it here
Edit : I have modified my code and used map func, now the output that I get is:
name1 groupid1 groupid2 ... so on
name2 groupid1 groupid2 ... so on
.
.
. and so on
where as I want :
name1 groupid1
name2 groupid2
.
.
.
You can display the data using destructuring assignment like the one you use in your code.
Eg:
const { name, groups } = item;
const { fields } = item.groups;
keyExtractor assign a unique key value to your render items. In your case, it assign a name value (from your this.props.people) to each items in your Flatlist.
As you know, all react children needs a unique key or you will get this warning
Warning: Each child in a list should have a unique "key" prop
The below code needs to be added
if(item.groups){
groupList = item.groups.map((unit, key)=>{
return (<View key="key">
<Text>{unit.id}</Text>
{ unit.fields.map((unit1, key1)=>{
return <Text key={key1}>{unit1.name}</Text>
})
}
and later we need to display groupList
return (
<View>
<View style={styles.cardContainerStyle}>
<View style={{ paddingRight: 5 }}>
<Text style={styles.cardTextStyle}>
{/* {name} {"\n"} */}
{groupList}
</Text>
</View>
<Image
style={styles.faceImageStyle}
// source={{ uri: picture.medium }}
/>
</View>
</View>
);
The above snippet will display data inside fields.
I'm trying to create a nested Flatlist(Flatlist inside another Flatlist), with dynamic data that is fetched from webservices.
Once the inner flatlist data gets fetched, basing on the content I am trying to increase the parent flatlist item height.
Can somebody guide me to solve the problem
The below is the view I am working
From App.js
renderItem = ({ item }) => {
if (item.empty === true) {
return <View style={[styles.item, styles.itemInvisible]} />;
}
return (
<View style={styles.item}>
<Text style={styles.itemText}>Name</Text>
<Text style={styles.itemText}>{item.key}</Text>
<InnerListView updateHeight={this.updateHeight} />
</View>
);
};
render() {
return (
<FlatList
data={[
{ key: 'ABCDEFGH' }
]}
style={styles.container}
renderItem={this.renderItem}
numColumns={numColumns}
/>
);
}
From InnerList
find_dimesions(layout){
const {x, y, width, height} = layout;
console.warn("xpos"+ x);
console.warn("ypos"+y);
console.warn("width"+width);
console.warn("height"+height);
this.props.updateHeight(height)
}
renderItem = ({ item, index }) => {
if (item.empty === true) {
return (<View style={[styles.item, styles.itemInvisible]} />);
}
return (
<View style={styles.item}>
<Text style={styles.itemText}>Name</Text>
<Text style={styles.itemText}>{item.key}</Text>
</View>
);
};
render() {
return (
<FlatList
onLayout={(event) => { this.find_dimesions(event.nativeEvent.layout) }}
data={formatData(data, numColumns)}
style={styles.container}
renderItem={this.renderItem}
numColumns={numColumns}
/>
);
}
I am expecting the parent view i.e redView height need to be increased.
First of all, an FYI: you could create this exact layout with a single SectionList as well if you want.
But to answer your question to get the outer flatlist to update its content / layout when the inner gets updated, you could make use of the extraData prop from FlatList. This prop allows the FlatList to update once its value changes. So the extraData you provide to the outer FlatList should be the same as the data for the inner FlatList.
My child component is below
export default function PickerList ({headingText,listData,hideView,finalpickedItem,onItemSelected,selected} ) {
const {modalHolder,modalHeader,modalHeaderText,modalBody,PerListView,PerListVie wText,okCancel,okCancelHolder,PerListViewActive,
} = styles;
return(
<View style={modalHolder}>
<View style={modalHeader}>
<Text style={modalHeaderText}>{headingText}</Text>
</View>
<View style={modalBody}>
<FlatList data={listData} renderItem={({item , index}) =>
<TouchableOpacity
onPress={() => {
{headingText === 'name'?
onItemSelected(item.name)
: headingText === 'Time' ?
onItemSelected(item.time)
: headingText === 'Property'
onItemSelected(item.property)
}
}}
style={{width:'100%',}}
>
<View
style={
selected===item.name ? PerListViewActive : PerListView > // here i am not getting active hot reload making it active
<Text style={PerListViewText}>
{item.name }
</Text></View>
</TouchableOpacity>
}
keyExtractor={(item, index) => index.toString()}
/>
</View>
</View>
);
};
PickerList.propTypes = {
onItemSelected: PropTypes.func.isRequired,
};
and my parent is
onMenuItemSelected = item => {
console.log(item); // i am getting here selected item
this.setState({ commonSelected: item }); // i am getting final state also.
}
<PickerList
headingText="Property"
listData = {this.state.property_type_options}
hideView = {()=>{this.hideView()}}
finalpickedItem = {()=>{this.finalpickedItem()}}
onItemSelected={this.onMenuItemSelected}
selected = {this.state.commonSelected} /// i have final value here also
/>
issue is "selected" not working every thing is working fine .. selected working but after a hot reload. can i re-render module. state is updating fine but it is not getting active.
I found my answer this is very nice.
extraData={this.state}
I have a map function on my app:
list.map((item, index) => {
return (
<View key={index}>
<Text
style={ this.state.active ? listStyle.listDone : listStyle.list } onPress={() => this.changeStatus(index)}
>
{item}
</Text>
</View>
)
})
I want to change the color when the current item i is clicked.
Since you want to select multiple items so instead of controlling the elements using single state value, use an array.
Store the selected indexes in the state array, whenever user click on any new item put it's index in that array otherwise remove it, and during ui creation check whether index in present in array or not and on the basis of that specify the color.
How to add or remove elements from array?
For that you need to pass a extra parameter in onClick function "the index of item" and by using that index add/remove elements.
Step1:
Define indexes = [] in state;
Step2:
list.map((item, index) => {
return (
<View key={index}>
<Text
style={ this.state.indexes[index] ? listStyle.listDone : listStyle.list }
onPress={() => this.changeStatus(index)}> {item} </Text>
</View>
)
})
onclick(index) {
let indexes = this.state.indexes.slice(0);
if(indexes.indexOf(index) == -1)
indexes.push(index);
else{
let id = indexes.indexOf(index);
indexes.splice(id, 1)
}
this.setState({indexes});
}
See this sample working jsfiddle: https://jsfiddle.net/mayankshukla5031/17h4Lz5u/