React Native - Flatlist Single Select - javascript

I've been searching around how to make a single select in Flatlist but I can't find any, here in my code I'am trying to make a single select on every cells that is inside my Flatlist.
Example: I select cell no.1, then no.1 will be selected. And if I need to select no.2, both no.1 and no.2 will be selected.
What is happening in my code is when I select no.1, it would select all cells.
export default class Dishes extends Component {
constructor(props) {
super (props)
this.state = {
data: [],
id: [],
price: [],
count: 0,
SplOrder: '',
tbl: this.props.navigation.state.params.tbl,
orderDet: this.props.navigation.state.params.orderDet,
DineIn: this.props.navigation.state.params.DineIn,
TakeOut: this.props.navigation.state.params.TakeOut,
}
}
/********************EDIT*************************
_incrementCount() {
this.setState({ count: this.state.count + 1 });
}
_decreaseCount() {
this.setState({ count: this.state.count - 1 });
}
changeTextHandler() {
this.setState({ ['count'+index]: text });
};
*/
_renderItem = ({ item, index }) => {
return (
<View>
<View>
<View>
<Text>Name: { item.menu_desc }</Text>
<View}>
<Text>Price: ₱{ item.menu_price }</Text>
<Text>Status: { item.menu_status }</Text>
</View>
<TextInput
placeholder = "Insert Special Request"
onChangeText = { (text) => this.setState({ SplOrder: text }) }
value = { this.state.SplOrder }
/>
</View>
<View>
<TouchableOpacity
onPress = {() => this._incrementCount()}>
<Text> + </Text>
</TouchableOpacity>
<TextInput
onChangeText={this.changeTextHandler}
value={this.state['count'+index].toString()} // Not working
placeholder="0"/>
<TouchableOpacity
onPress = {() => this._decreaseCount()}>
<Text> - </Text>
</TouchableOpacity>
</View>
</View>
</View>
)
}
render() {
return (
<View>
<View>
<Text>Table No: { this.state.tbl }</Text>
<Text>Order No: { this.state.orderDet }</Text>
<Text>{ this.state.DineIn }{ this.state.TakeOut }</Text>
</View>
<FlatList
data = {this.state.data}
keyExtractor={(item, index) => index.toString()}
extraData={this.state}
renderItem = {this._renderItem}
/>
<View>
<TouchableOpacity
onPress = {() => this.submit()}>
<Text>Send Order</Text>
</TouchableOpacity>
</View>
</View>
)
}
}

The TextInputs are targeting the same state SplOrder so, if you change it in any TextInput the others will display the same. The solution I see is to put an state for each item you create.
You should do this:
<TextInput
placeholder = "Insert Special Request"
onChangeText = { (text) => this.setState({ ['SplOrder'+index]: text }) }
value = { this.state['SplOrder'+index] }
/>
The second problem we have discussed in the comments should be fixed by passing index parameter to incrementCount function.
Hi, now in the method _incrementCount() you will have to pass the index and increment each count with it index as you have in the value of. So you could do
<TouchableOpacity
onPress = {() => this._incrementCount(index)}>
<Text> + </Text>
</TouchableOpacity>
And then change your _incrementCount method adding a parameter and doing like this:
_incrementCount(index) {
this.setState({ ['count'+index]: this.state['count'+index] + 1 });
}

Related

React Native: FlatList Opens Modal for all Items Instead of Selected Item

I am using React Native FlatList and React Native Modal.
Upon clicking on the item from the FlatList, I want to view 1 Modal only (containing the details of the item selected).
However, if there are 4 items in the FlatList, selecting 1 item causes
all 4 modals to pop up.
Is there anyway I can display only 1 modal for 1 selected item in the FlatList instead of multiple modal?
Code Snippet below (some lines of code were removed as it's not needed):
constructor(props) {
super(props);
this.state = {
dataSource: [],
isLoading: true,
modalVisible: false,
}
}
setModalVisible = (visible) => {
this.setState({ modalVisible: visible });
}
viewModal(item, price) {
const { modalVisible } = this.state;
return (
<Modal
statusBarTranslucent={true}
animationType={"slide"}
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
}}
>
<View>
<View>
<View>
<Text>
Appointment Start Time:
</Text>
<Text>
{moment(item.time_start).format('h:mm a')}
</Text>
</View>
<View>
<Text>
Appointment End Time:
</Text>
<Text>
{moment(item.end_start).format('h:mm a')}
</Text>
</View>
<View style={styles.row}>
<Text>
Total:
</Text>
<Text>
{price}
</Text>
</View>
<View>
<View>
<Button
mode="outlined"
onPress={() => {
this.setModalVisible(!modalVisible);
}}
>
{'Cancel'}
</Button>
</View>
<View>
<Button
mode="contained"
onPress={() => {
this.setModalVisible(!modalVisible);
}}
>
{'Accept'}
</Button>
</View>
</View>
</View>
</View>
</Modal>
);
}
viewFreelancerTime() {
return (
<View>
<FlatList
renderItem={({ item }) => {
let totalPrice = (parseFloat(item.service_price) + parseFloat(item.service_deposit)).toFixed(2);
return (
<Container>
{this.viewModal(item, totalPrice)}
<TouchableNativeFeedback
onPress={() => {
this.setModalVisible(true);
}}
>
<View>
<View>
<Text>
{moment(item.time_start).format('h:mm a')}
</Text>
</View>
<View>
<Text>
{totalPrice}
</Text>
</View>
</View>
</TouchableNativeFeedback>
</Container>
);
}}
/>
</View>
);
}
render() {
return (
<>
<View style={{ flex: 1 }}>
{this.viewFreelancerTime()}
</View>
</>
);
};
The poblem is that you are rendering the modal in the renderItem method, so every time you select an item, the modal will open in each rendered item.
To solve that you will have to render a custom Modal component with an absolute position at the same level of your FlatList, and pass the selected item information as props.
UPDATE
Just something like this:
import React, {useState} from "react";
import { Modal } from "react-native";
export default function MyFlatList(props) {
const [selectedItem, setSelectedItem] = useState(null);
const handleOnSelectItem = (item) => {
setSelectedItem(item);
};
const handleOnCloseModal = () => {
setSelectedItem(null);
};
renderItem = ({ item }) => {
return (
<Container>
<TouchableNativeFeedback onPress={() => handleOnSelectItem(item)}>
<View>
<View>
<Text>{moment(item.time_start).format("h:mm a")}</Text>
</View>
<View>
<Text>{totalPrice}</Text>
</View>
</View>
</TouchableNativeFeedback>
</Container>
);
};
return (
<View>
<FlatList renderItem={this.renderItem} />
<CustomModal isVisible={selectedItem} selectedItem={selectedItem} onClose={handleOnCloseModal} />
</View>
);
}
export function CustomModal(props) {
const { isVisible, item, onClose, /*...*/ } = props;
// Play with the item data
let totalPrice = (
parseFloat(item.servicePrice) + parseFloat(item.serviceDeposit)
).toFixed(2);
return <Modal visible={isVisible} onRequestClose={onClose}>{/*...*/}</Modal>; // Render things inside the data
}
I suggest you to do a pagination and play with FlatList native props if you are going to implement an infinite scroll.
Pd: to reduce re-renders because of state updates, I am reusing the selectedItem state, so if it is not null then the modal will be visible

How pass state value like a props

i'd radio button component :
state = {
radioValue: null,
}
render() {
const { options,onPress } = this.props;
const { radioValue } = this.state;
return (
<View style={{flexDirection: 'row'}}>
{options.map(item => {
return (
<View key={item.key} style={styles.buttonContainer}>
<TouchableOpacity
style={styles.circle}
onPress={() => {
this.setState({
radioValue: item.key,
});
}}
>
{radioValue === item.key && <View style={styles.checkedCircle} />}
</TouchableOpacity>
<Text>{item.text}</Text>
</View>
);
})}
</View>
);
then i use this component in an another like this :
<RadioButton options={options} />
How can i use the value of my state in the second component ??
Thx !!
The best way would be to move the state from RadioButton to the outer component:
// RadioButton Component
render() {
const { options, onPress, value } = this.props;
return (
<View style={{flexDirection: 'row'}}>
{options.map(item => {
return (
<View key={item.key} style={styles.buttonContainer}>
<TouchableOpacity
style={styles.circle}
onPress={() => onPress(item.key)} // onPress comes from parent
>
{value === item.key && <View style={styles.checkedCircle} />}
</TouchableOpacity>
<Text>{item.text}</Text>
</View>
);
})}
</View>
);
// Parent component
state = {
radioValue: null,
}
onPress = (value) => {
this.setState({
radioValue: value,
});
}
render() {
// ...
<RadioButton
options={options}
onPresss={this.onPress}
value={radioValue}
/>
// ...
}
If you can't do that, another approach would be using render props. This would let you keep the state in the child, and pass it to the parent. Not recommended, though, as it is more confusing.

how can we update single state on two tab change in react native

Please help me for this .whole class code I have shared .
In this code there are two tabs ,
In first tab I am getting value from array and getting sum of T"ransactionAmount" and maping .All these values I am passing in state sum .
Now In second tab there is one input field where I have to wite any number .
Condition : - when I am on tab first then sum on value should disply in below button and when I go to second tab than whatever value I enter that only come in state sum.
this sum value I am getting in below button .
Basically there is one state sum where I am updating value on tab change .But in first tab value is coming fine but I am going to second tab and entering tha vale ,then its coming with first tabe value .
Below link is refrence for my ui and screenshot for details .
Please help.
First tabe pic
https://xd.adobe.com/view/d733da48-5d0c-47ca-7ded-6fc8f0f609cf-a102/screen/37cb15c6-b56a-4b98-8612-e9b86d0dd34c/Android-Mobile-147/?fullscreen
// Below is array value
financialTransactionDetail: Array(3)
0:
AdjustedAmount: "0"
NetTransactionAmount: "1050"
TransactionAmount: 1050
1:
AdjustedAmount: "0"
NetTransactionAmount: "1050"
TransactionAmount: 1050
2:
AdjustedAmount: "0"
NetTransactionAmount: "1050"
Status: "Unpaid"
TransactionAmount: 1050
class PaymentsInvoice extends Component {
constructor(props) {
super(props);
const { navigation,invoiceDetailsinfo } = this.props;
const{financialTransactionDetail} = invoiceDetailsinfo;
this.state = {
title: 'Payments against invoice',
icon: 'sim',
mobile:navigation.state.params.customer.service.serviceNumber,
checked: financialTransactionDetail.financialTransactionDetail.map(() => true),
sum:0,
}
}
handleChange(index){
let newChecked = this.state.checked;
newChecked[index] = !newChecked[index];
this.setState({checked: newChecked})
}
handleChangeSum = (sumValue) => {
this.setState({
sum: parseInt(sumValue)
});
}
render() {
let { title, icon, mobile,invoice_specific,sum } = this.state;
const { navigation,invoiceDetailsinfo } = this.props;
const{financialTransactionDetail} = invoiceDetailsinfo;
this.state.checked.map((value, index) => {
if(value) {
sum += financialTransactionDetail.financialTransactionDetail[index].TransactionAmount;
}
});
return (
<View style={styles.imgBG}>
<ScrollView keyboardShouldPersistTaps='always' showsVerticalScrollIndicator={false}>
<View style={styles.container}>
<View>
<Header title={title} icon={icon} subtitle={mobile} navigation={navigation} />
</View>
<View style={styles.dataContainer}>
<Tabs >
<Tab heading="INVOICE SPECIFIC" tabLabel="SPECIFIC">
{ !_.isEmpty(financialTransactionDetail.financialTransactionDetail) && financialTransactionDetail.financialTransactionDetail.map(
(data, index) => {
const formatedate = data.DueDate;
const formateDate = formatedate.split(' ')[0];
return(
<View key={index} style={{flexDirection:'row', padding:10, alignItems:'center', justifyContent:'space-between',backgroundColor:'#fff'}}>
<View style={{paddingRight:10, marginRight:10}}>
<CheckBox style={styles.checkBox} color="#00678f" checked={this.state.checked[index]} onPress={() =>this.handleChange(index)}/>
</View>
<View style={{flexDirection:'column',flex:1, padding:10, borderWidth:1, borderColor:'lightgrey', borderRadius:3}}>
<View style={{flexDirection:'row', alignItems:'center'}}>
{!this.state.checked[index] && <RegularText text={`₦ ${data.TransactionAmount}`} style={{paddingBottom:10, paddingRight:5}}/>}
<SmallText text={`Due by ${(formateDate)}`} style={{paddingBottom:10}}/>
</View>
{this.state.checked[index] &&
<RegularText text={`₦ ${data.TransactionAmount}`} style={{borderColor: '#00fff', borderBottomWidth:1}}>
</RegularText>
}
</View>
</View>
)
}
)
}
</Tab>
<Tab heading="AD-HOC" tabLabel="AD-HOC">
<View style={{flexDirection:'column', padding:10, backgroundColor:'#fff', minHeight:deviceHeight }}>
<RegularText text="Enter Specific Amount to pay" style={{paddingBottom:5}} textColor="#959595"/>
<View>
<Item style={{borderColor: '#00fff', borderBottomWidth:1}}>
<Input
autoFocus={true}
onPress={() => this.handleChange('sumValue')}
onChangeText={(sumValue) => this.handleChangeSum(sumValue)}
/>
</Item>
</View>
</View>
</Tab>
</Tabs>
</View>
</View>
</ScrollView>
<View style={{bottom:0,position:'absolute', width: '100%'}}>
<Button full onPress={()=>navigation.navigate('PaymentOptionsContainer',sum)}>
<Text>Receive Payment ({sum})</Text>
</Button>
</View>
</View>
);
}
}
export default PaymentsInvoice;
Thanks :)
You should use single source of truth i-e include the state in parent component , So when the parent component re renders it gets the value from its own state and pass it on to the child component .

React Native pass data into modal

I'm still new in react native and programming, and i am trying to pass items from my flat list into a modal. What i'm about to pass is the icon, status, and description. How am i supposed to do that?
this is my flatlist
buildPanel(index, item) {
let panel = [];
let keys = DBkeys['Requests'].MyRequest;
let status = item[keys['status']];
panel.push(<View style={{ position: 'absolute', right: 0, bottom: 0, padding: normalize(5), alignItems: 'center' }} key={'status'}>
<TouchableOpacity onPress={this.handleShowModal}>
<Icon name={img.itemStatus[status].name} type={img.itemStatus[status].type} color={img.itemStatus[status].color} size={normalize(38)} />
</TouchableOpacity>
</View>);
return panel;
}
<View style={[styles.panelContainer, status === 'success' ? {} : { backgroundColor: color.white }]}>
<FlatList
showsVerticalScrollIndicator={false}
progressViewOffset={-10}
refreshing={this.state.refreshing}
onRefresh={this.onRefresh.bind(this)}
onMomentumScrollEnd={(event) => event.nativeEvent.contentOffset.y === 0 ? this.onRefresh() : null}
data={content}
renderItem={({ item }) => item}
keyExtractor={(item, key) => key.toString()}
/>
</View>
<IconModal visible={this.state.modalVisible} close={this.handleDismissModal}/>
and this is my IconModal.js
const IconModal = (props) => {
return(
<Modal
isVisible={props.visible}
onBackdropPress={props.close}
>
<View style={styles.dialogBox}>
<View style={styles.icon}>
<Icon name='open-book' type='entypo' color='#ffb732' size={normalize(70)} />
</View>
<View style={styles.text}>
<Text style={styles.status}>Status</Text>
<Text>Desc</Text>
</View>
<TouchableOpacity onPress={props.close}>
<View>
<Text style={styles.buttonText}>GOT IT</Text>
</View>
</TouchableOpacity>
</View>
</Modal>
)
}
IconModal.propTypes ={
visible: PropTypes.bool.isRequired,
close: PropTypes.func,
}
from the renderItem of your FlatList,
You must be clicking somewhere to open modal,
when you click store that whole single item in state variable,
like, if you're using TouchableOpacity then
<TouchableOpacity onPress={this.passDataToModal}/>
...
...
passDataToModal=(item)=>{
this.setState({modalData:item},()=>{
//you can open modal here
});
}
and in your modal component,
you can pass data with prop.
<IconModal modalData={this.state.modalData} visible={this.state.modalVisible} close={this.handleDismissModal}/>
and you can use these data in IconModal as this.props.modalData.
If there is more data then you can always add another prop.
Define the following Hooks in your function Component.
const [modalVisible, setModalVisible] = useState(false);
const [modalData, setModalData] = useState([]);
const [modalTitle, setModalTitle] = useState('');
Now Trigger the function which opens the Modal, while simultaneously passing data into it.
<TouchableHighlight underlayColor="skyblue" onPress={() => { openSettingsModal(title,settings) } }>
Open Modal
</TouchableHighlight>
Here is the function code -
const openSettingsModal = (title,settings) => {
setModalTitle(title);
setModalData(settings);
setModalVisible(!modalVisible);
}
And finally a snippet of the Modal Code.
<Modal animationType="none" transparent={true} visible={modalVisible} >
<View style={styles.centeredView}>
<Text> { modalTitle }</Text>
<Text> { modalData }</Text>
</View>
</Modal>
For example:
class Container extends Component {
constructor(props) {
super(props)
this.state = {
modalVisible: false,
activeItemName: '', //state property to hold item name
activeItemId: null, //state property to hold item id
}
}
openModalWithItem(item) {
this.setState({
modalVisible: true,
activeItemName: item.name,
activeItemId: item.id
})
}
render() {
let buttonList = this.props.item.map(item => {
return (
<TouchableOpacity onPress={() => { this.openModalWithItem(item) }}>
<Text>{item.name}</Text>
</TouchableOpacity>
)
});
return (
<View>
{/* Example Modal Component */}
<Modal isOpen={this.state.openDeleteModal}
itemId={this.state.activeItemId}
itemName={this.state.activeItemName} />
{buttonList}
</View>
)
}
}

how to render objects in react native?

I am working on a simple to do application linked to firebase using react native. i have one class with a few methods in it,as far as I can tell by searching online about this problem, it seems to be something related to an output in the render function. but i checked my methods and I am unable to pin down the exact problem.
and this is my class:
class ToDo_React extends Component {
constructor(props) {
super(props);
var myFirebaseRef = new Firebase(' ');
this.itemsRef = myFirebaseRef.child('items');
this.state = {
newTodo: '',
todoSource: new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2})
};
this.items = [];
}
componentDidMount() {
// When a todo is added
this.itemsRef.on('child_added', (dataSnapshot) => {
this.items.push({id: dataSnapshot.key(), text: dataSnapshot.val()});
this.setState({
todoSource: this.state.todoSource.cloneWithRows(this.items)
});
});
this.itemsRef.on('child_removed', (dataSnapshot) => {
this.items = this.items.filter((x) => x.id !== dataSnapshot.key());
this.setState({
todoSource: this.state.todoSource.cloneWithRows(this.items)
});
});
}
addTodo() {
if (this.state.newTodo !== '') {
this.itemsRef.push({
todo: this.state.newTodo
});
this.setState({
newTodo : ''
});
}
}
removeTodo(rowData) {
this.itemsRef.child(rowData.id).remove();
}
render() { return (
<View style={styles.appContainer}>
<View style={styles.titleView}>
<Text style={styles.titleText}>
My Todos
</Text>
</View>
<View style={styles.inputcontainer}>
<TextInput style={styles.input} onChangeText={(text) => this.setState({newTodo: text})} value={this.state.newTodo}/>
<TouchableHighlight
style={styles.button}
onPress={() => this.addTodo()}
underlayColor='#dddddd'>
<Text style={styles.btnText}>Add!</Text>
</TouchableHighlight>
</View>
<ListView
dataSource={this.state.todoSource}
renderRow={this.renderRow.bind(this)} />
</View>
);
}
renderRow(rowData) {
return (
<TouchableHighlight
underlayColor='#dddddd'
onPress={() => this.removeTodo(rowData)}>
<View>
<View style={styles.row}>
<Text style={styles.todoText}>{rowData.text}</Text>
</View>
<View style={styles.separator} />
</View>
</TouchableHighlight>
);
}
}
The issue is with your renderRow method where you render:
<Text style={styles.todoText}>{rowData.text}</Text>
Here you are passing an object as a children of Text element instead of a string. That is because you're setting an object under the text key for your data store here:
this.items.push({id: dataSnapshot.key(), text: dataSnapshot.val()});
Note that val() here is a reference to an object you've pushed to your firebase instance here (see firebase docs):
this.itemsRef.push({
todo: this.state.newTodo
});
So what you perhaps want to do here is to just push this.state.newTodo instead of an object.

Categories