Remove item from a list in react-native - react-native

I'm displaying a list of items on a page which i provide users the ability to delete.
With my code when the users taps on delete i get this error
undefined is not an object (evaluating this.state.myGroups)
JS
handleExistGroup(item) {
var array = [...this.state.myGroups];
var index = array.indexOf(item.target.value)
if (index !== -1) {
array.splice(index, 1);
this.setState({myGroups: array});
}
}
Array
state = {
myGroups : [
{
name: 'Karate Group',
description: 'Test Group',
icon: 'https://s3.amazonaws.com/uifaces/faces/twitter/brynn/128.jpg'
},
{
name: 'Choir Group',
description: 'Test 2',
icon: 'https://s3.amazonaws.com/uifaces/faces/twitter/brynn/128.jpg'
}
]
}
VIEW
<View style={styles.container}>
<ScrollView >
{
groupList.map((item, i) => {
return (
<View key={i} style={styles.user}>
<Card >
<ListItem
roundAvatar
title={item.name}
avatar={{uri:'https://s3.amazonaws.com/uifaces/faces/twitter/brynn/128.jpg'}}
/>
<View>
<Button
containerViewStyle={{width:'50%', alignSelf:'flex-end', position:"absolute", top:0, right:-25}}
onPress={()=>(handleExistGroup(item))}
rounded = {true}
style={{margin:10}}
icon={{name: 'trash'}}
backgroundColor='#DC143C'
buttonStyle={{borderRadius: 0, marginLeft: 0, marginRight: 0, marginBottom: 0}}
title='Exit Group'
/>
</View>
<Text style={{alignSelf:'center', padding:5, fontFamily:'HelveticaNeue-Light', fontSize:16}}>
Jonied: 24th January, 2019
</Text>
</Card>
</View>
);
})
}
</ScrollView>
</View>
How do i make it work so it can delete the particular row a use want to delete from the array?

You need to bind handleExistGroup() function with this in your constructor.
constructor(props) {
super(props);
this.handleExistGroup = this.handleExistGroup.bind(this);
}

Related

Add a button "see more" in FlatList?

I use flatList to make a list of elements. I would like to show 15 elements and then add a button "see more" to show the next 15 etc.
I was about tu use this tutorial : https://aboutreact.com/react-native-flatlist-pagination-to-load-more-data-dynamically-infinite-list/
But I don't need to use fetch, I already have set up the data (state.listData) and in fact, I'm a little lost on how to adapt it...
I thought that maybe anyone could help me a little.
Thanks a lot
this.state = {
selectedId: '',
setSelectedId:'',
listData:''
}
};
renderItem = ({ item }) => {
const backgroundColor = item.id === this.selectedId ? "transparent" : "fff";
return (
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('UpdateTripsForm')}
style={{ backgroundColor }}
/>
<Image source={require("../../assets/images/arrow.png")} style={{width: 15, height:15, justifyContent: 'center'}}/>
</View>
);
};
initListData = async () => {
let list = await getFlights(0);
if (list) {
this.setState({
listData: list
});
}
};
render() {
return (
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.listData}
renderItem={this.renderItem}
maxToRenderPerBatch={15}
keyExtractor={(item) => item.id}
extraData={this.selectedId}
/>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
{i18n.t("tripsform.action.back")}
</Text>
</View>
<Image
source={require("../../assets/images/btn-background.png")}
style={styles.tripsimg2}
/>
</TouchableOpacity>
</SafeAreaView>
);
};
}
I just tried this thanks to #Pramod 's answer :
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.flightsListitem, style]}>
<Text style={styles.h4}>{item.id}</Text>
</TouchableOpacity>
);
export default class FlightsList extends Component {
constructor(props) {
super(props);
this.state = {
selectedId: '',
setSelectedId:'',
listData:'',
page:1,
perPage:2,
loadMoreVisible:true,
displayArray:[]
}
};
renderItem = ({ item }) => {
const backgroundColor = item.id === this.selectedId ? "transparent" : "fff";
return (
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('UpdateTripsForm')}
style={{ backgroundColor }}
/>
<Image source={require("../../assets/images/arrow.png")} style={{width: 15, height:15, justifyContent: 'center'}}/>
</View>
);
};
initListData = async () => {
let list = await getFlights(0);
if (list) {
this.setState({
listData: list
});
}
};
componentDidMount(){
this.setNewData()
// console.log(tempArray)
}
setNewData(){
var tempArray=[]
if(this.state.listData.length == this.state.displayArray.length){
this.setState({
loadMoreVisible:false
})
}else{
for(var i=0; i<(this.state.page*this.state.perPage); i++){
tempArray.push(this.state.listData)
}
this.setState({
displayArray: tempArray,
loadMoreVisible:true
})
}
}
loadMore(){
this.setState({
page: this.state.page+1
},()=>{
this.setNewData()
})
}
async UNSAFE_componentWillMount() {
this.initListData();
}
render() {
return (
<ImageBackground
source={require("../../assets/images/background.jpg")}
style={styles.backgroundImage}
>
<Header
backgroundImage={require("../../assets/images/bg-header.png")}
backgroundImageStyle={{
resizeMode: "stretch",
}}
centerComponent={{
text: i18n.t("mytrips.title"),
style: styles.headerComponentStyle,
}}
containerStyle={[styles.headerContainerStyle, { marginBottom: 0 }]}
statusBarProps={{ barStyle: "light-content" }}
/>
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.displayArray}
renderItem={this.renderItem}
keyExtractor={(item) => item.id}
extraData={this.selectedId}
/>
{this.state.loadMoreVisible == true?
<Button style={{width:'100%', height:10, backgroundColor:'green', justifyContent:'center', alignItems:'center'}}
title = 'load more'
onPress={()=>{this.loadMore()}}>
</Button>:null}
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
{i18n.t("tripsform.action.back")}
</Text>
</View>
<Image
source={require("../../assets/images/btn-background.png")}
style={styles.tripsimg2}
/>
</TouchableOpacity>
</SafeAreaView>
</ImageBackground>
);
};
}
the flatlist is not displayed : I get :
You can user pagination method with per page limit so that you can have granular control
Load the array per page when component mount
On every click increase the per page and based on per page update data of your flat list
And also put a flag which will check when the data has ended which will help to hide the load more button when data ends
Working example: https://snack.expo.io/#msbot01/suspicious-orange
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
SafeAreaView,
SectionList,
Switch,
FlatList
} from 'react-native';
import Constants from 'expo-constants';
import Icon from 'react-native-vector-icons/FontAwesome';
import AwesomeIcon from 'react-native-vector-icons/FontAwesome';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
page:1,
perPage:2,
loadMoreVisible:true,
DATA: [{
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
title: 'First Item',
},
{
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
title: 'Second Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'Third Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'fourth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'fifth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29sd72',
title: 'sixth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29dr72',
title: 'seventh Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d7w2',
title: 'Eight Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29ad72',
title: 'Nineth Item',
},
{
id: '58694a0f-3da1-471f-bd96-14557d1e29d72',
title: 'Tenth Item',
}],
displayArray:[]
}
}
componentDidMount(){
this.setNewData()
// console.log(tempArray)
}
setNewData(){
var tempArray=[]
if(this.state.DATA.length == this.state.displayArray.length){
this.setState({
loadMoreVisible:false
})
}else{
for(var i=0; i<(this.state.page*this.state.perPage); i++){
tempArray.push(this.state.DATA[i])
}
this.setState({
displayArray: tempArray,
loadMoreVisible:true
})
}
}
loadMore(){
this.setState({
page: this.state.page+1
},()=>{
this.setNewData()
})
}
render() {
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.state.displayArray}
renderItem={({item})=>
<View style={{flexDirection:'row'}}>
<Text style={{fontSize:20}}>{item.title} </Text>
</View>
}
keyExtractor={item => item.id}
/>
{this.state.loadMoreVisible == true?
<View style={{width:'100%', height:10, backgroundColor:'green', justifyContent:'center', alignItems:'center'}} onClick={()=>{this.loadMore()}}>Load more</View>:null
}
</View>
);
}
}
Set data in state (already done ==> this.state.listData)
Set counter in state (initialize with 1)
Set 15 first elements in state (you can name it "renderedData" or something like that) and then increase cuonter to 1
Add a function that increases the "renderedData" by 15 items by increasing the counter by one
Add Footer component to the list which will call the function you created in stage 3
To take only 15( or 30/45/60 etc..) items from the list you can do something like this:
this.setState({ renderedItem: listData.slice(0, counter*15) })

Breakline after a item of an array reactnative

I have item that are in array. I want to change the line after setting the first item and so on. I am getting the values in a row. But I want it in column. I am getting value as:
015245088 9823178404 9851108404
But I want value as:
015245088
9823178404
9823178404
I have implemented as follows:
this.state = {
contact: [
{
id: 0,
name: '015245088'
},
{
id: 1,
name: '9823178404'
},
{
id: 2,
name: '9851108404'
}
]
}
<CardSection>
<FontAwesomeIcon style={styles.contentStyle} icon={faPhone} />
{
this.state.contact.map((item, index) => (
<TouchableOpacity
key={item.id}
style={styles.opacityStyle}
onPress={()=>Linking.openURL(`tel:${item.name}`)}>
<Text style={styles.contactStyle}>{item.name} </Text>
</TouchableOpacity>
))
}
</CardSection>
this.state = {
contact: [
{
id: 0,
name: '015245088'
},
{
id: 1,
name: '9823178404'
},
{
id: 2,
name: '9851108404'
}
]
}
<CardSection>
<FontAwesomeIcon style={styles.contentStyle} icon={faPhone} />
<View style={{flexDirection:'column'}}>
{
this.state.contact.map((item, index) => (
<TouchableOpacity
key={item.id}
style={styles.opacityStyle}
onPress={()=>Linking.openURL(`tel:${item.name}`)}>
<Text style={styles.contactStyle}>{item.name} </Text>
</TouchableOpacity>
}
</View>
</CardSection>
You need to change the flexDirection to column.
So, Just add the following styles to styles.contentStyle. Or wrap your map function with a View and add the following styles to it.
flexDirection: 'column'
flex: 1

Get the input data from TextInputs which are programatically added using a button

I've a screen where there is a button to add textInputs. Any no. of inputs can be added by the user. There is another button named submit. When it is tapped, how can I get the appropriate input values. I need them in array eg: [{name1, designation1}, {name2, designation2}, ...].
Code:
App.js
export default class App extends React.Component {
state = {
myArr: []
}
_onPressOut() {
let temp = index ++
this.state.myArr.push(temp)
this.setState({
myArr: this.state.myArr
})
}
_getData() {
//how can I get the data from input values here?
}
render() {
let Arr = this.state.myArr.map((a, i) => {
return <NewComponent />
})
return (
<ScrollView>
<View style={styles.container}>
<Text>Event 1st</Text>
{ Arr }
<Text>Eventlast</Text>
</View>
<TouchableOpacity onPress={() => this._onPressOut()}>
<Text style={{ color: 'green' }}>Add New Component</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => this._getData()}>
<View style={{ backgroundColor: 'blue', marginTop: 30 }}>
<Text style={{ color: 'white', textAlign: 'center', marginVertical: 10 }}>Submit</Text>
</View>
</TouchableOpacity>
</ScrollView>
);
}
}
NewComponent.js
class NewComponent extends React.Component{
state = {
name: '',
designation: '',
}
onNameChange = (text) => {
this.setState({
name: text,
});
}
render () {
return (
<View style={{ borderTopWidth:2, borderBottomColor: 'red', paddingTop: 20, marginTop: 30 }}>
<TextInput
placeholder={'Enter Your Name'}
onChangeText={text => {
this.onNameChange(text);
// this.onPropValueChange('SignUpName', text);
}}
value={this.state.name}
style={[{borderBottomColor:'red', borderBottomWidth: 1}]}
/>
<TextInput
placeholder={'Designation'}
onChangeText={text => {
this.onDesignationChange(text);
// this.onPropValueChange('SignUpDesignation', text)
}
}
value={this.state.designation}
style={[{borderBottomColor:'red', borderBottomWidth: 1}]}
/>
</View>
);
}
}
Considering the following assumptions that,until the name is filled,the designation cannot be filled and until the one set of name and designation are filled, the next set of inputs should not be rendered,
In NewComponent.js for the destinationTextInput, make the following changes.
<TextInput
placeholder={'Designation'}
onChangeText={text => {
this.onDesignationChange(text);
// this.onPropValueChange('SignUpDesignation', text)
}
}
value={this.state.designation}
style={[{borderBottomColor:'red', borderBottomWidth: 1}]}
onBlur = {() => {this.props.onNameAndDesignationAdded(this.state.name,this.state.designation)}}
/>
And in App.js add the following
in state object, introduce a new state called resultArr as follows:
state = {
myArr: [],
resultArr : []
}
The _getData function will be as follows:
_getData(name,designation) {
//how can I get the data from input values here?
if(name,designation) {
let tempArr = this.state.resultArr;
tempArr.push({name, designation})
this.setState({resultArr : tempArr})
}
}
The NewComponent called in App.js will have a callback from the TextInput of destination input onBlur method.
let Arr = this.state.myArr.map((a, i) => {
return <NewComponent onNameAndDesignationAdded = {(name,designation) => {
this._getData(name,designation)
} } />
})

React Native FlatList Touchable Opacity

I used FlatList to display the title that is in the data. I added in TouchableOpacity for the FlatList. So for example, when I click 'First', I want to show the the data from 'mood' and it will show the list of passionate,rousing and confident. I want to show the list on a new file/screen and show purely the mood of list.
This is my data:
const Mock =
[
{ title: 'First',
mood:
[
{name: 'passionate'},
{name: 'rousing'},
{name: 'confident'},
],
},
{ title: 'Second',
mood:
[
{name: 'rollicking'},
{name: 'cheerful'},
{name: 'fun'},
{name: 'sweet'},
{name: 'amiable'},
{name: 'natured'}
],
This is my FlatList code:
export default class Cluster1 extends Component{
render() {
return (
<View>
<FlatList
data={Mock}
renderItem={({ item, index }) => {
return <FlatListItem item={item} index={index} />;
}}
/>
</View>
);
}
}
class FlatListItem extends Component {
render() {
return (
<View style={styles.list}>
<View>
<TouchableOpacity>
<Text style={styles.itemTitle}>{this.props.item.title}</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
What should I do with the TouchableOpacity when I want to show the mood name when I click the title?
This is my style code
const styles = StyleSheet.create({
itemTitle:{
fontSize: 25,
fontWeight: 'bold',
color: 'white',
margin: 20,
},
},
list:{
flex: 1,
backgroundColor: '#00BCD4',
alignItems: 'center',
justifyContent: 'space-between',
flexDirection: 'row',
},
});
You should modify your code as below to do this:
export default class Cluster1 extends Component {
render() {
return (
<View style={{ margin: 30, backgroundColor: '#ddd' }}>
<FlatList
data={Mock}
renderItem={({ item, index }) => {
return <FlatListItem item={item} index={index} />;
}}
/>
</View>
);
}
}
class FlatListItem extends Component {
state = { showItemIndex: [false, false] };
_onPress = index => () => {
let showItemIndex = this.state.showItemIndex;
showItemIndex[index] = !this.state.showItemIndex[index];
this.setState({ showItemIndex });
};
render() {
return (
<View style={styles.list}>
<View>
<TouchableOpacity onPress={this._onPress(this.props.index)}>
<Text style={styles.itemTitle}>{this.props.item.title}</Text>
</TouchableOpacity>
{this.state.showItemIndex[this.props.index] && (
<FlatList
data={this.props.item.mood}
extraData={this.state.showItemIndex}
renderItem={({ item, index }) => {
return (
<Text item={item} index={index}>
{item.name}
</Text>
);
}}
/>
)}
</View>
</View>
);
}
}
Use this, it's should be work fine for you:
Link: https://github.com/oblador/react-native-collapsible
Link: https://github.com/naoufal/react-native-accordion
Link: https://github.com/cuiyueshuai/react-native-expandable-section-flatlist
You could set a state variable which gets updated whenever your TouchableOpacity gets pressed. And then you conditionally render the title or the list of mood names:
class FlatListItem extends Component {
constructor(props) {
super(props);
this.state = {collapsed: true}
}
render() {
return (
<View style={styles.list}>
<View>
<TouchableOpacity
onPress={this.onPress}
>
<Text style={styles.itemTitle}>
{this.props.item.title}
</Text>
</TouchableOpacity>
{this.state.collapsed ?
<View /> :
<View>
{this.props.item.mood.map(mood => <Text>{mood.name}</Text>)}
</View>}
</View>
</View>
);
}
onPress = () => {
this.setState({collapsed: !this.state.collapsed})
}
}
You can do this by separating out the datasource that is required to show in the list.
First to display title: you can do something like this
export default class Cluster1 extends Component {
state = {
data: []
};
componentWillMount() {
const titles = Mock.forEach(data => data.title);
this.setState({
data: titles
});
}
onItemClick = item => {
const itemIndex = Mock.findIndex(data => (
data.title === item
));
const values = Mock[itemIndex].mood;
this.setState({
data: values
});
};
render() {
return (
<View>
<FlatList
data={this.state.data}
renderItem={({ item, index }) => {
return (
<FlatListItem
item={item}
index={index}
onItemClick={this.itemClick}
/>
);
}}
/>
</View>
);
}
}
and then in your FlatlistItem code:
class FlatListItem extends Component {
render() {
return (
<View style={styles.list}>
<View>
<TouchableOpacity onPress={() =>
this.props.onItemClick(this.props.item)
}>
<Text style={styles.itemTitle}>
{
this.props.item.name ?
this.props.item.name : this.props.item
}
</Text>
</TouchableOpacity>
</View>
</View>
);
}
}

Invariant violation nothing was returnded from render

renderAvail(user, sectionId, rowId, highlightRow){
return(
<ImageBackground source={require('../../../asset/available_card_medium.png')} style={styles.containerA}>
if(user.type == 'Wear'){
return(
<Image
source={require('../../../icon.png')}
resizeMode = 'cover' style={styles.thumbImage}>)
}
<View style={styles.body}>
<Text style={styles.message1}>{user.desc}</Text>
<Text style={styles.message1}>+{user.points} <Image source={require('../../../icon_large.png')} resizeMode ='contain' style ={{height:50,width:60}}/></Text>
</View>
<ImageButton
appearance={{
normal: require("../../../asset/btn.png"),
highlight: require("../../../asset/btn2.png")}}
onPress={ this.onPressButton }/>
</ImageBackground>
);
}
{
id: 0,
type: 'Survey',
desc: 'Keep Your Apple Watch on for 8 Hours Today',
title: 'Test1',
date: '09/06/2018',
},
{
id: 1,
type: 'Wear',
desc: 'Keep Your Apple Watch on for 8 Hours Today',
title: 'Test1',
date: '09/06/2018',
},
{
id: 2,
type: 'meal',
desc: 'Keep Your Apple Watch on for 8 Hours Today',
title: 'Test1',
date: '09/06/2018',
},
{
id: 3,
type: 'Survey',
desc: 'Keep Your Apple Watch on for 8 Hours Today',
title: 'Test1',
date: '09/06/2018',
},
{
id: 4,
type: 'Wear',
desc: 'Keep Your Apple Watch on for 8 Hours Today',
title: 'Test1',
date: '09/06/2018',
}
];
You cant return use return directly inside a return block because it will be already returned if condition meets. instead of above code, you can use this.
return (<ImageBackground source={require('../../../asset/available_card_medium.png')} style={styles.containerA}>
{user.type == 'Wear' && <Image
source={require('../../../icon.png')}
resizeMode='cover' style={styles.thumbImage}>)
}
<View style={styles.body}>
<Text style={styles.message1}>{user.desc}</Text>
<Text style={styles.message1}>+{user.points} <Image source={require('../../../icon_large.png')} resizeMode='contain' style={{ height: 50, width: 60 }} /></Text>
</View>
<ImageButton
appearance={{
normal: require("../../../asset/btn.png"),
highlight: require("../../../asset/btn2.png")
}}
onPress={this.onPressButton} />
</ImageBackground>
);
Going through your code i can understand that you are new to react native.
As #Ranvir Gorai answered previously, you can not use if statement inside a render(). please spare some time and go through JSX elements.
Assuming you are calling a function renderAvail() from render of a class.
import React from 'react';
import {
View,
Text,
// add other elements which are used.
} from 'react-native';
export default class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
user:'some data',
sectionId:'some data',
rowId: 'some data',
highlightRow: 'some data',
}
}
// add the data to state using this.setState({key,value});
renderAvail() {
return (
<ImageBackground source={require('../../../asset/available_card_medium.png')} style={styles.containerA}>
{user.type == 'Wear' && <Image
source={require('../../../icon.png')}
resizeMode='cover' style={styles.thumbImage}>)
}
<View style={styles.body}>
<Text style={styles.message1}>{user.desc}</Text>
<Text style={styles.message1}>+{user.points} <Image source={require('../../../icon_large.png')} resizeMode='contain' style={{ height: 50, width: 60 }} /></Text>
</View>
<ImageButton
appearance={{
normal: require("../../../asset/btn.png"),
highlight: require("../../../asset/btn2.png")
}}
onPress={this.onPressButton} />
</ImageBackground>
);
}
render() {
return (
{this.renderAvail(this.state.user, this.state.sectionId, this.state.rowId, this.state.highlightRow)}
);
}
}