React Native FlatList:Select 所有项目和 运行 'OnPress'
React Native FlatList : Select all Items and run 'OnPress'
我正在使用 react-native-bouncy-checkbox
和 Flatlist
。
I have created an array object which has id
, name
, amount
.
So far I have achieved:
User can select individual items from the Flatlist
, and it will add the amount
and display it
as total amount.
User can also edit the amount
they have selected using TextInput.
但是,我正在尝试创建“Select All”功能。
因此,当用户按下 'Select All' 或点击 'checkbox' 时,它应该:
- select FlatList
中的所有项目
- 加总金额
- 允许用户单独编辑所有 selected 数量
- 更新复选框以显示它是 selected。
到目前为止,我已尝试让所有 'checkbox' 显示当按下 'Select All' 文本或按下 'checkbox' 时(在 select所有文字)。
过去几个小时我一直在努力让它工作,但没能成功。因此,欢迎就此问题提供任何帮助。
下面提供的代码片段和应用程序屏幕截图:
代码示例:
import 'react-native-gesture-handler';
import React, { useState, useEffect, Component } from 'react';
import { StyleSheet, View, Text, TouchableOpacity, FlatList } from 'react-native';
import { Button, Divider, TextInput } from 'react-native-paper';
import BouncyCheckbox from 'react-native-bouncy-checkbox';
import TextInputMask from 'react-native-text-input-mask';
function AccountMultiplePayment({ navigation }) {
const [apiData, setApiData] = useState([
{
id: 1,
name: 'John',
address: 'address 1',
amount: '79.90',
},
{
id: 2,
name: 'Simon',
address: 'address 2',
amount: '35.50',
},
{
id: 3,
name: 'Tim',
address: 'address 3',
amount: '15.50',
},
{
id: 4,
name: 'Rob',
address: 'address 4',
amount: '33.33',
},
{
id: 5,
name: 'Sarah',
address: 'address 5',
amount: '77.77',
},
])
const [billPaymentAmount, setBillPaymentAmount] = useState({})
const [selectedBill, setSelectedBill] = useState([]);
const [totalPaymentAmount, setTotalPaymentAmount] = useState(0);
const computeBillPaymentAmount = () => {
let newBillPaymentAmount = {}
apiData.forEach(({ id, amount }) => {
newBillPaymentAmount[id] = amount
})
return newBillPaymentAmount
}
const computeTotalPaymentAmount = () => {
let total = 0
selectedBill.forEach(id => {
total += parseFloat(billPaymentAmount[id])
})
// Prevent NaN issue, because once user delete amount will become empty string
return total ? total : 0
}
useEffect(() => {
setBillPaymentAmount(computeBillPaymentAmount())
}, [apiData])
useEffect(() => {
setTotalPaymentAmount(computeTotalPaymentAmount())
}, [selectedBill, billPaymentAmount])
const [checked, setChecked] = useState(false);
return (
<>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
style={{ alignItems: 'center', paddingVertical: 10 }}
onPress={() => setChecked(!checked)}
>
<Text style={{ color: 'black', fontSize: 25 }}>Select All</Text>
</TouchableOpacity>
<BouncyCheckbox
isChecked={checked}
fillColor={'green'}
unfillColor={'#FFFFFF'}
onPress={() => {
setChecked(!checked)
}}
/>
</View>
<FlatList
data={apiData}
renderItem={({ item }) => {
return (
<View style={{ flexDirection: 'row' }}>
<View style={[styles.subHeaderContainer, { flex: 1 }]}>
<Text
style={[
styles.defaultText,
{ fontWeight: 'bold', fontSize: 16 },
]}>
{item.name}
</Text>
<Divider style={{ marginVertical: 5 }} />
<View style={{ flexDirection: 'row' }}>
<Text
style={[styles.defaultText, { fontWeight: 'bold', flex: 2 }]}>
Total Payable Amount:
</Text>
<View style={{ flex: 1 }}>
<TextInput
value={billPaymentAmount[item.id]}
onChangeText={value => setBillPaymentAmount({ ...billPaymentAmount, [item.id]: value })}
keyboardType={'numeric'}
mode={'outlined'}
label={'RM'}
dense={true}
render={props =>
<TextInputMask
{...props}
mask='[9990].[99]'
/>
}
/>
</View>
</View>
</View>
<BouncyCheckbox
isChecked={checked}
fillColor={'green'}
unfillColor={'#FFFFFF'}
onPress={() => {
if (selectedBill.includes(item.id)) {
setSelectedBill(selectedBill.filter(value => value !== item.id))
} else {
setSelectedBill([...selectedBill, item.id])
}
}}
/>
</View>
);
}}
keyExtractor={item => item.id}
removeClippedSubviews={false}
/>
{
<>
<View
style={{
paddingVertical: 10,
paddingHorizontal: 20,
flexDirection: 'row',
backgroundColor:'blue'
}}>
<Text
style={{ color: 'white', flex: 1, fontWeight: 'bold', fontSize: 18 }}>
Total Amount:{' '}
</Text>
<View>
<Text style={{ color: 'white', fontWeight: 'bold', fontSize: 24 }}>
RM {totalPaymentAmount.toFixed(2)}
</Text>
{totalPaymentAmount <= 0 ? null : (
<TouchableOpacity
onPress={() => {
//navigation.goBack();
navigation.goBack();
navigation.navigate('Account');
}}>
<Text>Reset</Text>
</TouchableOpacity>
)}
</View>
</View>
<Button
mode={'contained'}
color={'limegreen'}
style={{
borderRadius: 5,
marginHorizontal: 20,
marginVertical: 10,
justifyContent: 'center',
}}
labelStyle={{ color: 'white', padding: 10 }}
uppercase={false}
onPress={() => { }}
disabled={totalPaymentAmount <= 0 ? true : false}>
<Text>Pay Bill</Text>
</Button>
</>
}
</>
);
}
class Account extends Component {
constructor(props) {
super(props);
this._isMounted = false;
this.state = {
};
}
render() {
return (
<>
{<AccountMultiplePayment {...this.props} {...this.navigation} />}
</>
);
}
}
export default Account;
const styles = StyleSheet.create({
flex: {
flex: 1,
},
headerTitle: {
alignItems: 'center',
borderBottomLeftRadius: 25,
borderBottomRightRadius: 25,
paddingHorizontal: 20,
paddingVertical: 20,
},
subHeaderContainer: {
paddingVertical: 10,
paddingHorizontal: 20,
backgroundColor: 'white',
borderRadius: 10,
elevation: 5,
marginVertical: 5,
marginHorizontal: 10,
},
subHeaderTitle: {
color: 'white',
fontWeight: 'bold',
fontSize: 16,
backgroundColor: '#2c1855',
padding: 10,
borderRadius: 10,
},
defaultText: {
color: 'black',
},
});
这是目前的样子。所有选中的项目都单独 selected:
这就是我想要实现的目标:
检查 documentation of react-native-bouncy-checkbox 后,以下内容很重要。
isChecked
确定默认的内部复选框状态。只评估一次。
check
状态处理由库内部处理。
- 我们想自己处理这个问题,以便通过单个状态更改选中所有复选框。为此,我们需要将
disableBuiltInState
属性设置为 true “如果你想手动处理 isChecked 属性并禁用内置处理”。
因此,我建议采用以下工作流程。
- 为
isChecked
创建一个状态数组,其中包含每个元素的布尔标志。
- 创建自定义
onPress
-handler,它从 FlatList
获取索引作为参数。使用该索引,我们能够将状态数组中的正确布尔标志设置为 true
或 false
.
- 如果按下
Select All
,我们的 onPress
-处理程序会将我们状态数组的所有布尔标志设置为 true
。这将导致我们的屏幕重新渲染,并且由于我们将 disableBuiltInState
属性设置为 true
,react-native-bouncy-checkbox 将使用我们的自定义状态处理。
这是一个最小的工作示例。
// set inititial all to false, you might want some different initial state
const [checks, setChecks] = useState(Array.from({ length: apiData.length }, () => false))
const onCheck = React.useCallback(
(index) => {
let previous = [...checks]
previous[index] = !previous[index]
setChecks(previous)
},
[checks, setChecks]
)
const selectAll = React.useCallback(() => {
let previous = [...checks]
setChecks(previous.map(() => true))
}, [checks, setChecks])
return (
<View style={{ padding: 40, marginTop: 50 }}>
<FlatList
data={apiData}
renderItem={({ item, index }) => (
<View style={{ padding: 20 }}>
<BouncyCheckbox
isChecked={checks[index]}
fillColor={"green"}
unfillColor={"#FFFFFF"}
onPress={() => onCheck(index)}
disableBuiltInState={true}
/>
</View>
)}
keyExtractor={(item) => "" + item.id}
removeClippedSubviews={false}
/>
<TouchableOpacity onPress={selectAll}>
<Text style={{ fontSize: 20, color: "red" }}> Select All</Text>
</TouchableOpacity>
</View>
)
看起来如下。
按 Select All
得到
由于您想计算附加值(每个选定项目的总应付金额),我建议只将 checks
状态添加到您已经实施的 useEffect
。当 checks
状态改变时,这个 useEffect
将被调用。您可以计算 checks
中的布尔标志为真的所有字段,并设置输入字段的状态。
重构代码如下
import "react-native-gesture-handler";
import React, { useState, useEffect, Component } from "react";
import {
StyleSheet,
View,
Text,
TouchableOpacity,
FlatList,
} from "react-native";
import { Button, Divider, TextInput } from "react-native-paper";
import BouncyCheckbox from "react-native-bouncy-checkbox";
function AccountMultiplePayment({ navigation }) {
const [apiData, setApiData] = useState([
{
id: 1,
name: "John",
address: "address 1",
amount: "79.90",
},
{
id: 2,
name: "Simon",
address: "address 2",
amount: "35.50",
},
{
id: 3,
name: "Tim",
address: "address 3",
amount: "15.50",
},
{
id: 4,
name: "Rob",
address: "address 4",
amount: "33.33",
},
{
id: 5,
name: "Sarah",
address: "address 5",
amount: "77.77",
},
]);
const [billPaymentAmount, setBillPaymentAmount] = useState({});
const [selectedBill, setSelectedBill] = useState([]);
const [totalPaymentAmount, setTotalPaymentAmount] = useState(0);
const computeBillPaymentAmount = () => {
let newBillPaymentAmount = {};
apiData.forEach(({ id, amount }) => {
newBillPaymentAmount[id] = amount;
});
return newBillPaymentAmount;
};
const computeTotalPaymentAmount = () => {
let total = 0;
selectedBill.forEach((id) => {
total += parseFloat(billPaymentAmount[id]);
});
// Prevent NaN issue, because once user delete amount will become empty string
return total ? total : 0;
};
useEffect(() => {
setBillPaymentAmount(computeBillPaymentAmount());
}, [selectedBill.length]);
useEffect(() => {
setTotalPaymentAmount(computeTotalPaymentAmount());
}, [billPaymentAmount]);
const selectAllBill = () => {
if (selectedBill.length < apiData.length) {
setSelectedBill([...new Set(apiData.map((item) => item.id))]);
}
if (selectedBill.length === apiData.length) {
setSelectedBill([]);
}
};
const isBillAdded = (id) => selectedBill.some((el) => el === id);
const hasAllBillselected = apiData.length === selectedBill.length;
return (
<>
<View style={{ flexDirection: "row" }}>
<TouchableOpacity
style={{ alignItems: "center", paddingVertical: 10 }}
onPress={selectAllBill}
>
<Text style={{ color: "black", fontSize: 25 }}>Select All</Text>
</TouchableOpacity>
<BouncyCheckbox
disableBuiltInState
isChecked={hasAllBillselected}
fillColor={"green"}
unfillColor={"#FFFFFF"}
onPress={selectAllBill}
/>
</View>
<FlatList
data={apiData}
renderItem={({ item }) => {
return (
<View style={{ flexDirection: "row" }}>
<View style={[styles.subHeaderContainer, { flex: 1 }]}>
<Text
style={[
styles.defaultText,
{ fontWeight: "bold", fontSize: 16 },
]}
>
{item.name}
</Text>
<Divider style={{ marginVertical: 5 }} />
<View style={{ flexDirection: "row" }}>
<Text
style={[
styles.defaultText,
{ fontWeight: "bold", flex: 2 },
]}
>
Total Payable Amount:
</Text>
<View style={{ flex: 1 }}>
<TextInput
value={billPaymentAmount[item.id]}
onChangeText={(value) =>
setBillPaymentAmount({
...billPaymentAmount,
[item.id]: value,
})
}
keyboardType={"numeric"}
mode={"outlined"}
label={"RM"}
dense={true}
/>
</View>
</View>
</View>
<BouncyCheckbox
disableBuiltInState
isChecked={selectedBill.includes(item.id)}
fillColor={"green"}
unfillColor={"#FFFFFF"}
onPress={() => {
if (selectedBill.includes(item.id)) {
setSelectedBill(
selectedBill.filter((value) => value !== item.id)
);
} else {
setSelectedBill([...new Set([...selectedBill, item.id])]);
}
}}
/>
</View>
);
}}
keyExtractor={(item) => item.id}
removeClippedSubviews={false}
/>
{
<>
<View
style={{
paddingVertical: 10,
paddingHorizontal: 20,
flexDirection: "row",
backgroundColor: "blue",
}}
>
<Text
style={{
color: "white",
flex: 1,
fontWeight: "bold",
fontSize: 18,
}}
>
Total Amount:{" "}
</Text>
<View>
<Text
style={{ color: "white", fontWeight: "bold", fontSize: 24 }}
>
RM {totalPaymentAmount.toFixed(2)}
</Text>
{totalPaymentAmount <= 0 ? null : (
<TouchableOpacity
onPress={() => {
//navigation.goBack();
navigation.goBack();
navigation.navigate("Account");
}}
>
<Text>Reset</Text>
</TouchableOpacity>
)}
</View>
</View>
<Button
mode={"contained"}
color={"limegreen"}
style={{
borderRadius: 5,
marginHorizontal: 20,
marginVertical: 10,
justifyContent: "center",
}}
labelStyle={{ color: "white", padding: 10 }}
uppercase={false}
onPress={() => {}}
disabled={totalPaymentAmount <= 0 ? true : false}
>
<Text>Pay Bill</Text>
</Button>
</>
}
</>
);
}
class Account extends Component {
constructor(props) {
super(props);
this._isMounted = false;
this.state = {};
}
render() {
return (
<>{<AccountMultiplePayment {...this.props} {...this.navigation} />}</>
);
}
}
export default Account;
const styles = StyleSheet.create({
flex: {
flex: 1,
},
headerTitle: {
alignItems: "center",
borderBottomLeftRadius: 25,
borderBottomRightRadius: 25,
paddingHorizontal: 20,
paddingVertical: 20,
},
subHeaderContainer: {
paddingVertical: 10,
paddingHorizontal: 20,
backgroundColor: "white",
borderRadius: 10,
elevation: 5,
marginVertical: 5,
marginHorizontal: 10,
},
subHeaderTitle: {
color: "white",
fontWeight: "bold",
fontSize: 16,
backgroundColor: "#2c1855",
padding: 10,
borderRadius: 10,
},
defaultText: {
color: "black",
},
});
我正在使用 react-native-bouncy-checkbox
和 Flatlist
。
I have created an array object which has
id
,name
,amount
.So far I have achieved:
User can select individual items from the
Flatlist
, and it will add theamount
and display it as total amount.User can also edit the
amount
they have selected using TextInput.
但是,我正在尝试创建“Select All”功能。
因此,当用户按下 'Select All' 或点击 'checkbox' 时,它应该:
- select FlatList 中的所有项目
- 加总金额
- 允许用户单独编辑所有 selected 数量
- 更新复选框以显示它是 selected。
到目前为止,我已尝试让所有 'checkbox' 显示当按下 'Select All' 文本或按下 'checkbox' 时(在 select所有文字)。
过去几个小时我一直在努力让它工作,但没能成功。因此,欢迎就此问题提供任何帮助。
下面提供的代码片段和应用程序屏幕截图:
代码示例:
import 'react-native-gesture-handler';
import React, { useState, useEffect, Component } from 'react';
import { StyleSheet, View, Text, TouchableOpacity, FlatList } from 'react-native';
import { Button, Divider, TextInput } from 'react-native-paper';
import BouncyCheckbox from 'react-native-bouncy-checkbox';
import TextInputMask from 'react-native-text-input-mask';
function AccountMultiplePayment({ navigation }) {
const [apiData, setApiData] = useState([
{
id: 1,
name: 'John',
address: 'address 1',
amount: '79.90',
},
{
id: 2,
name: 'Simon',
address: 'address 2',
amount: '35.50',
},
{
id: 3,
name: 'Tim',
address: 'address 3',
amount: '15.50',
},
{
id: 4,
name: 'Rob',
address: 'address 4',
amount: '33.33',
},
{
id: 5,
name: 'Sarah',
address: 'address 5',
amount: '77.77',
},
])
const [billPaymentAmount, setBillPaymentAmount] = useState({})
const [selectedBill, setSelectedBill] = useState([]);
const [totalPaymentAmount, setTotalPaymentAmount] = useState(0);
const computeBillPaymentAmount = () => {
let newBillPaymentAmount = {}
apiData.forEach(({ id, amount }) => {
newBillPaymentAmount[id] = amount
})
return newBillPaymentAmount
}
const computeTotalPaymentAmount = () => {
let total = 0
selectedBill.forEach(id => {
total += parseFloat(billPaymentAmount[id])
})
// Prevent NaN issue, because once user delete amount will become empty string
return total ? total : 0
}
useEffect(() => {
setBillPaymentAmount(computeBillPaymentAmount())
}, [apiData])
useEffect(() => {
setTotalPaymentAmount(computeTotalPaymentAmount())
}, [selectedBill, billPaymentAmount])
const [checked, setChecked] = useState(false);
return (
<>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
style={{ alignItems: 'center', paddingVertical: 10 }}
onPress={() => setChecked(!checked)}
>
<Text style={{ color: 'black', fontSize: 25 }}>Select All</Text>
</TouchableOpacity>
<BouncyCheckbox
isChecked={checked}
fillColor={'green'}
unfillColor={'#FFFFFF'}
onPress={() => {
setChecked(!checked)
}}
/>
</View>
<FlatList
data={apiData}
renderItem={({ item }) => {
return (
<View style={{ flexDirection: 'row' }}>
<View style={[styles.subHeaderContainer, { flex: 1 }]}>
<Text
style={[
styles.defaultText,
{ fontWeight: 'bold', fontSize: 16 },
]}>
{item.name}
</Text>
<Divider style={{ marginVertical: 5 }} />
<View style={{ flexDirection: 'row' }}>
<Text
style={[styles.defaultText, { fontWeight: 'bold', flex: 2 }]}>
Total Payable Amount:
</Text>
<View style={{ flex: 1 }}>
<TextInput
value={billPaymentAmount[item.id]}
onChangeText={value => setBillPaymentAmount({ ...billPaymentAmount, [item.id]: value })}
keyboardType={'numeric'}
mode={'outlined'}
label={'RM'}
dense={true}
render={props =>
<TextInputMask
{...props}
mask='[9990].[99]'
/>
}
/>
</View>
</View>
</View>
<BouncyCheckbox
isChecked={checked}
fillColor={'green'}
unfillColor={'#FFFFFF'}
onPress={() => {
if (selectedBill.includes(item.id)) {
setSelectedBill(selectedBill.filter(value => value !== item.id))
} else {
setSelectedBill([...selectedBill, item.id])
}
}}
/>
</View>
);
}}
keyExtractor={item => item.id}
removeClippedSubviews={false}
/>
{
<>
<View
style={{
paddingVertical: 10,
paddingHorizontal: 20,
flexDirection: 'row',
backgroundColor:'blue'
}}>
<Text
style={{ color: 'white', flex: 1, fontWeight: 'bold', fontSize: 18 }}>
Total Amount:{' '}
</Text>
<View>
<Text style={{ color: 'white', fontWeight: 'bold', fontSize: 24 }}>
RM {totalPaymentAmount.toFixed(2)}
</Text>
{totalPaymentAmount <= 0 ? null : (
<TouchableOpacity
onPress={() => {
//navigation.goBack();
navigation.goBack();
navigation.navigate('Account');
}}>
<Text>Reset</Text>
</TouchableOpacity>
)}
</View>
</View>
<Button
mode={'contained'}
color={'limegreen'}
style={{
borderRadius: 5,
marginHorizontal: 20,
marginVertical: 10,
justifyContent: 'center',
}}
labelStyle={{ color: 'white', padding: 10 }}
uppercase={false}
onPress={() => { }}
disabled={totalPaymentAmount <= 0 ? true : false}>
<Text>Pay Bill</Text>
</Button>
</>
}
</>
);
}
class Account extends Component {
constructor(props) {
super(props);
this._isMounted = false;
this.state = {
};
}
render() {
return (
<>
{<AccountMultiplePayment {...this.props} {...this.navigation} />}
</>
);
}
}
export default Account;
const styles = StyleSheet.create({
flex: {
flex: 1,
},
headerTitle: {
alignItems: 'center',
borderBottomLeftRadius: 25,
borderBottomRightRadius: 25,
paddingHorizontal: 20,
paddingVertical: 20,
},
subHeaderContainer: {
paddingVertical: 10,
paddingHorizontal: 20,
backgroundColor: 'white',
borderRadius: 10,
elevation: 5,
marginVertical: 5,
marginHorizontal: 10,
},
subHeaderTitle: {
color: 'white',
fontWeight: 'bold',
fontSize: 16,
backgroundColor: '#2c1855',
padding: 10,
borderRadius: 10,
},
defaultText: {
color: 'black',
},
});
这是目前的样子。所有选中的项目都单独 selected:
这就是我想要实现的目标:
检查 documentation of react-native-bouncy-checkbox 后,以下内容很重要。
isChecked
确定默认的内部复选框状态。只评估一次。check
状态处理由库内部处理。- 我们想自己处理这个问题,以便通过单个状态更改选中所有复选框。为此,我们需要将
disableBuiltInState
属性设置为 true “如果你想手动处理 isChecked 属性并禁用内置处理”。
因此,我建议采用以下工作流程。
- 为
isChecked
创建一个状态数组,其中包含每个元素的布尔标志。 - 创建自定义
onPress
-handler,它从FlatList
获取索引作为参数。使用该索引,我们能够将状态数组中的正确布尔标志设置为true
或false
. - 如果按下
Select All
,我们的onPress
-处理程序会将我们状态数组的所有布尔标志设置为true
。这将导致我们的屏幕重新渲染,并且由于我们将disableBuiltInState
属性设置为true
,react-native-bouncy-checkbox 将使用我们的自定义状态处理。
这是一个最小的工作示例。
// set inititial all to false, you might want some different initial state
const [checks, setChecks] = useState(Array.from({ length: apiData.length }, () => false))
const onCheck = React.useCallback(
(index) => {
let previous = [...checks]
previous[index] = !previous[index]
setChecks(previous)
},
[checks, setChecks]
)
const selectAll = React.useCallback(() => {
let previous = [...checks]
setChecks(previous.map(() => true))
}, [checks, setChecks])
return (
<View style={{ padding: 40, marginTop: 50 }}>
<FlatList
data={apiData}
renderItem={({ item, index }) => (
<View style={{ padding: 20 }}>
<BouncyCheckbox
isChecked={checks[index]}
fillColor={"green"}
unfillColor={"#FFFFFF"}
onPress={() => onCheck(index)}
disableBuiltInState={true}
/>
</View>
)}
keyExtractor={(item) => "" + item.id}
removeClippedSubviews={false}
/>
<TouchableOpacity onPress={selectAll}>
<Text style={{ fontSize: 20, color: "red" }}> Select All</Text>
</TouchableOpacity>
</View>
)
看起来如下。
按 Select All
得到
由于您想计算附加值(每个选定项目的总应付金额),我建议只将 checks
状态添加到您已经实施的 useEffect
。当 checks
状态改变时,这个 useEffect
将被调用。您可以计算 checks
中的布尔标志为真的所有字段,并设置输入字段的状态。
重构代码如下
import "react-native-gesture-handler";
import React, { useState, useEffect, Component } from "react";
import {
StyleSheet,
View,
Text,
TouchableOpacity,
FlatList,
} from "react-native";
import { Button, Divider, TextInput } from "react-native-paper";
import BouncyCheckbox from "react-native-bouncy-checkbox";
function AccountMultiplePayment({ navigation }) {
const [apiData, setApiData] = useState([
{
id: 1,
name: "John",
address: "address 1",
amount: "79.90",
},
{
id: 2,
name: "Simon",
address: "address 2",
amount: "35.50",
},
{
id: 3,
name: "Tim",
address: "address 3",
amount: "15.50",
},
{
id: 4,
name: "Rob",
address: "address 4",
amount: "33.33",
},
{
id: 5,
name: "Sarah",
address: "address 5",
amount: "77.77",
},
]);
const [billPaymentAmount, setBillPaymentAmount] = useState({});
const [selectedBill, setSelectedBill] = useState([]);
const [totalPaymentAmount, setTotalPaymentAmount] = useState(0);
const computeBillPaymentAmount = () => {
let newBillPaymentAmount = {};
apiData.forEach(({ id, amount }) => {
newBillPaymentAmount[id] = amount;
});
return newBillPaymentAmount;
};
const computeTotalPaymentAmount = () => {
let total = 0;
selectedBill.forEach((id) => {
total += parseFloat(billPaymentAmount[id]);
});
// Prevent NaN issue, because once user delete amount will become empty string
return total ? total : 0;
};
useEffect(() => {
setBillPaymentAmount(computeBillPaymentAmount());
}, [selectedBill.length]);
useEffect(() => {
setTotalPaymentAmount(computeTotalPaymentAmount());
}, [billPaymentAmount]);
const selectAllBill = () => {
if (selectedBill.length < apiData.length) {
setSelectedBill([...new Set(apiData.map((item) => item.id))]);
}
if (selectedBill.length === apiData.length) {
setSelectedBill([]);
}
};
const isBillAdded = (id) => selectedBill.some((el) => el === id);
const hasAllBillselected = apiData.length === selectedBill.length;
return (
<>
<View style={{ flexDirection: "row" }}>
<TouchableOpacity
style={{ alignItems: "center", paddingVertical: 10 }}
onPress={selectAllBill}
>
<Text style={{ color: "black", fontSize: 25 }}>Select All</Text>
</TouchableOpacity>
<BouncyCheckbox
disableBuiltInState
isChecked={hasAllBillselected}
fillColor={"green"}
unfillColor={"#FFFFFF"}
onPress={selectAllBill}
/>
</View>
<FlatList
data={apiData}
renderItem={({ item }) => {
return (
<View style={{ flexDirection: "row" }}>
<View style={[styles.subHeaderContainer, { flex: 1 }]}>
<Text
style={[
styles.defaultText,
{ fontWeight: "bold", fontSize: 16 },
]}
>
{item.name}
</Text>
<Divider style={{ marginVertical: 5 }} />
<View style={{ flexDirection: "row" }}>
<Text
style={[
styles.defaultText,
{ fontWeight: "bold", flex: 2 },
]}
>
Total Payable Amount:
</Text>
<View style={{ flex: 1 }}>
<TextInput
value={billPaymentAmount[item.id]}
onChangeText={(value) =>
setBillPaymentAmount({
...billPaymentAmount,
[item.id]: value,
})
}
keyboardType={"numeric"}
mode={"outlined"}
label={"RM"}
dense={true}
/>
</View>
</View>
</View>
<BouncyCheckbox
disableBuiltInState
isChecked={selectedBill.includes(item.id)}
fillColor={"green"}
unfillColor={"#FFFFFF"}
onPress={() => {
if (selectedBill.includes(item.id)) {
setSelectedBill(
selectedBill.filter((value) => value !== item.id)
);
} else {
setSelectedBill([...new Set([...selectedBill, item.id])]);
}
}}
/>
</View>
);
}}
keyExtractor={(item) => item.id}
removeClippedSubviews={false}
/>
{
<>
<View
style={{
paddingVertical: 10,
paddingHorizontal: 20,
flexDirection: "row",
backgroundColor: "blue",
}}
>
<Text
style={{
color: "white",
flex: 1,
fontWeight: "bold",
fontSize: 18,
}}
>
Total Amount:{" "}
</Text>
<View>
<Text
style={{ color: "white", fontWeight: "bold", fontSize: 24 }}
>
RM {totalPaymentAmount.toFixed(2)}
</Text>
{totalPaymentAmount <= 0 ? null : (
<TouchableOpacity
onPress={() => {
//navigation.goBack();
navigation.goBack();
navigation.navigate("Account");
}}
>
<Text>Reset</Text>
</TouchableOpacity>
)}
</View>
</View>
<Button
mode={"contained"}
color={"limegreen"}
style={{
borderRadius: 5,
marginHorizontal: 20,
marginVertical: 10,
justifyContent: "center",
}}
labelStyle={{ color: "white", padding: 10 }}
uppercase={false}
onPress={() => {}}
disabled={totalPaymentAmount <= 0 ? true : false}
>
<Text>Pay Bill</Text>
</Button>
</>
}
</>
);
}
class Account extends Component {
constructor(props) {
super(props);
this._isMounted = false;
this.state = {};
}
render() {
return (
<>{<AccountMultiplePayment {...this.props} {...this.navigation} />}</>
);
}
}
export default Account;
const styles = StyleSheet.create({
flex: {
flex: 1,
},
headerTitle: {
alignItems: "center",
borderBottomLeftRadius: 25,
borderBottomRightRadius: 25,
paddingHorizontal: 20,
paddingVertical: 20,
},
subHeaderContainer: {
paddingVertical: 10,
paddingHorizontal: 20,
backgroundColor: "white",
borderRadius: 10,
elevation: 5,
marginVertical: 5,
marginHorizontal: 10,
},
subHeaderTitle: {
color: "white",
fontWeight: "bold",
fontSize: 16,
backgroundColor: "#2c1855",
padding: 10,
borderRadius: 10,
},
defaultText: {
color: "black",
},
});