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.
Related
When pass the item to the next screen through navigator of radio.js the other screen attendence.js card shows all item details with same id in the card with multiple times when an C# API is called in attendancjs.
I want to show different items in the card when calling an API in attendance.js.
Radio.Js
onRadioBtnClick = () => {
data.map((item) =>
{
if(item.attendanceType==checked)
{
// if (index < item) {
// console.log(item)
// return item;
// }
// for(i=0;i<item.length;i++)
// {
// elements=item[i]
console.log({item})
// }
navigation.navigate('attendance',{item})
} }
);
enter image description here
Attendance.js
return (
<ScrollView>
{
// {`${post.attendanceTime}`+ `${post.attendanceType}`}
// console.log(item)
data.map(() => (
<View style={styles.view} >
<Card style={styles.cardWrapper} >
<View style={styles.card}>
<Text style={styles.title}>Key:{item.id} {'\n'}
Latitude:{item.latitude} {'\n'} Longitude:{item.longitude}{'\n'}
Attentance Time:{item.attendanceTime} {'\n'}
Attentance:{`${item.attendanceType}`}{item.attendanceType=="i"? (<Image style={styles.icon} source={require('../assests/checked.png')} /> ):(<Image style={styles.icon} source={require('../assests/delete.png')} />)}
{'\n'}
Employee Name: {item.userInfo.displayName}
</Text>
</View>
</Card>
</View>
))
You are passing object navigation.navigate('attendance',{item}) from Radio.js and in Attendance.js you are iterate over data array and display only item(which is one object) to length of data. So it will take length of data and display one item which is passed from Radio.js
When Item with id=1 selected
then the element appear below
the selected Item. And when
unselected the element disappear.
This is a list with map() method.
The element should be inside the
SrollView like Item
But isn’t a new Item
I have this code that can create a new Item below the selected Item but I don't want to create a new Item but only appear a custom View(element) like above.
Expo Snack> https://snack.expo.dev/#stefanosalexandrou/honest-cashew
You could store the selected index in a state, which you are already doing. Then, use conditional rendering in order to render a custom component below the selected item. Furthermore, if a selected item is pressed again, set the state to undefined.
The handleOnPress function.
function handleOnPress(idx) {
setSelectedId(prev => prev === idx ? undefined : idx)
}
The updated render function.
<View style={styles.container}>
<ScrollView>
<View>
{data.map((person, index) => {
const backgroundColor = index === selectedId ? "#6e3b6e" : "#f9c2ff";
return (
<View>
<TouchableOpacity
onPress={() => handleOnPress(index)}
style={{
padding:20,
backgroundColor: backgroundColor,
marginBottom:20,
}}
>
<Text>{person.name}</Text>
</TouchableOpacity>
{
index === selectedId ? <View style={{backgroundColor: "red", height: 100, width: "100%"}}><Text>Custom Super Component Visible on press of above item </Text></View> : null
}
</View>
);
})}
</View>
</ScrollView>
</View>
I have added a dummy component if the index is selected.
However, you might want to select multiple items one after the other and deselect them individually while having the same effect. For doing so, we change the state to store an array of indices instead.
The updated state and handleOnPress function.
const [selectedIndices, setSelectedIds] = useState([]);
function handleOnPress(idx) {
if (selectedIndices.includes(idx)) {
setSelectedIds(prev => prev.filter(i => i !== idx))
} else {
setSelectedIds(prev => [...prev, idx])
}
}
The updated render function.
return (
<View style={styles.container}>
<ScrollView>
<View>
{data.map((person, index) => {
const backgroundColor = selectedIndices.includes(index) ? "#6e3b6e" : "#f9c2ff";
return (
<View>
<TouchableOpacity
onPress={() => handleOnPress(index)}
style={{
padding:20,
backgroundColor: backgroundColor,
marginBottom:20,
}}
>
<Text>{person.name}</Text>
</TouchableOpacity>
{
selectedIndices.includes(index) ? <View style={{backgroundColor: "red", height: 100, width: "100%"}}><Text>Custom Super Component Visible on press of above item </Text></View> : null
}
</View>
);
})}
</View>
</ScrollView>
</View>
);
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>
)
})
How to get values from multiple TextInput fields coming from array passing by map function to single variable by entering text in them? When i try to enter text in second TextInput it replaces the value which i stored from first TextInput.
Secondly instead of this, if i use push() function, it doesn't give desired output. What I exactly want is that to get the values of these three InputText and to store in single variable using single OnChangeEvent (as I am using Map() function).
Below is the sample of code on which i am working in React-Native.
this.state={
checkboxAttCollection:[{"Id": 10, "AttibuteCollectionName": "PowerTest"}, {"Id": 22, "AttibuteCollectionName": "36W"}, {"Id": 23, "AttibuteCollectionName": "Test123"}],
getInputText:'',
getInputvariables:[],
onChangeTextComp=(TextValue,index)=>{
this.state.getInputText=TextValue
console.log("******",this.state.getInputvariables)
this.state.getInputvariables.push(this.state.getInputText)
console.log("******",this.state.getInputvariables)
{this.state.checkboxAttCollection.map((item, i) =>
return (<View key={item.AttibuteCollectionId} style={{ flexDirection: 'row',}}>
<TextInput style={{height:hp('5%'),width:wp('60%'),backgroundColor:'red',borderBottomWidth:2,paddingVertical:0}}
onChangeText={(text)=>this.onChangeTextComp(text,i)}
MultiInputText={true}/>
{/* </View> */}
</View>)
})}
Try this way
this.state={
......
inputVlaues: []
}
onChangeTextComp=(value,index)=>{
const inputData = [...this.state.inputVlaues];
inputData[index] = value;
this.setState(inputVlaues: inputData);
}
{this.state.checkboxAttCollection.map((item, i) =>
return
(<View key={item.AttibuteCollectionId} style={{ flexDirection: 'row',}}>
<TextInput
onChangeText={(text)=>this.onChangeTextComp(text,i)}
......
value={this.state.inputVlaues[i] || ""}
/>
</View>)
})}
as an option you can generate event handlers dynamically
const [form, setForm] = useState({})
const createHandler = (fieldName) => {
return (newFieldValue) => {
setForm((oldValue) => {
return {
...oldValue,
[fieldName]: newFieldValue
}
})
}
}
return (
<>
<TextInput onChange={createHandler('name')} />
<TextInput onChange={createHandler('surname')} />
</>
)
I have a basic article app like inshorts i am storing all the articles in database which i fetch on the opening and display them in card format.Now I want to implement that when a card id viewed it should get low priority and render at the end next time the app is opened on that mobile.
I have no clue how to implement this.
This is how i am currently rendering it
renderArtciles=()=>{
let len=this.state.dataSource.length;
return this.state.dataSource.map((item,i)=>{
this.state.id=item._id;
this.state.priority=item.priority;
this.state.views=item.views;
if (i == this.state.currentIndex-1)
{
return(
<Animated.View key={item._id} {...this.state.panResponder.panHandlers} style={this.state.swiped_pan.getLayout()}>
< View style={{ flex: 1,position:'absolute',height:height,width:width,backgroundColor:'white'}}>
< View style={styles.Imagebody}>
<Image source={{ uri:item.img.data }} style={styles.image} />
</View>
<View style={styles.inner}>
<Text>{item.body} i==={i}{this.state.currentIndex} </Text>
</View>
</View>
</Animated.View>
)
}
else if (i < this.state.currentIndex)
{
return null
}
if (i == this.state.currentIndex)
{
return(
<Animated.View key={item._id} {...this.state.panResponder.panHandlers} style={this.state.pan.getLayout()}>
< View style={{ flex: 1,position:'absolute',height:height,width:width,backgroundColor:'white'}}>
< View style={styles.Imagebody}>
<Image source={{ uri:item.img.data }} style={styles.image} />
</View>
<View style={styles.inner}>
<Text>{item.body} i==={i}{this.state.currentIndex} </Text>
</View>
</View>
</Animated.View>
)
}
else{
return(
<Animated.View key={item._id} >
< View style={{ flex: 1,position:'absolute',height:height,width:width,backgroundColor:'white'}}>
< View style={styles.Imagebody}>
<Image source={{ uri:item.img.data }} style={styles.image} />
</View>
<View style={styles.inner}>
<Text>{item.body} i==={i}{this.state.currentIndex} </Text>
</View>
</View>
</Animated.View>
)
}
}
).reverse()
}
You could make use of AsyncStorage to store which items have been viewed (and how many times) in a JSON object which you can increment every time an item is viewed, and then retrieve again when the app is opened (and store in some state variable). You can then work out your ordering/priority logic based on the number of views.
To store the items you would do something like this:
_storeData = async () => {
try {
var itemsJson = {items: [
{item_id: 'foo', view_count: 10},
{item_id: 'bar', view_count: 5}
]}
await AsyncStorage.setItem('ItemViews', JSON.stringify(itemsJson));
} catch (error) {
// Error saving data
}
};
And to retrieve the items on app open you would do something like this:
_retrieveData = async () => {
try {
const items = await AsyncStorage.getItem('ItemViews');
// Rest of your code
} catch (error) {
// Error retrieving data
}
};