Related
I have implemented tensor-flow camera functionalities for pose detection on a website and then hosted it on netlify. Link: https://uactivsite-mobile.netlify.app/.Also github link of the same: https://github.com/viveksgonal/uactivsite/blob/main/src/App.js
I am using this as webview on react-native app. The first time the app builds perfectly and the camera starts. But whenever I try to reload it or run npx react-native start the second time, the camera never opens.
If anyone knows where I'm going wrong, it would be pleasure if you provide the solution. Thank you.
Code is attached below for the react-native app part:
/* eslint-disable react-native/no-inline-styles */
import React, { useRef, useState } from 'react';
const exercises = [
{
name: 'High Knees',
total: 20,
index: 0
},
{
name: 'Jumping Jacks',
total: 25,
index: 1
},
{
name: 'Squats',
total: 20,
index: 2
},
]
import WebView from 'react-native-webview'
import {
View,
StyleSheet,
Text,
Image,
TouchableWithoutFeedback,
Modal
} from 'react-native';
import { useNavigation } from '#react-navigation/native';
const ExerciseCamera = () => {
const webviewRef = useRef(null)
const navigation = useNavigation();
const [speed, setSpeed] = useState(0)
const [reps, setReps] = useState(0)
const ex = 1
function getInjectableJSMessage(message) {
return `
(function() {
window.dispatchEvent(new MessageEvent('message', {
data: ${JSON.stringify(message)}
}));
})();
`;
}
function onMessage(data) {
let val = JSON.parse(data.nativeEvent.data)
if (val.type === 'reps') {
setReps(val.data.rep)
if (val.data.speed !== 0) {
setSpeed(val.data.speed)
}
}
else if (val.type === 'completed') {
navigation.navigate('dashboard', {
screen: 'completeddailyexercise',
});
}
else {
console.log(val.data.rep)
}
}
function sendDataToWebView(msg) {
webviewRef.current.injectJavaScript(
getInjectableJSMessage(msg)
);
}
return (
<View style={styles.container}>
<Modal
transparent={true}
visible={true}
>
<View style={styles.top_container}>
<TouchableWithoutFeedback
onPress={() => {
navigation.navigate('dashboard', {
screen: 'completeddailyexercise',
});
}}>
<Image
style={styles.icons_container}
source={require('../../Assets/play.png')}
/>
</TouchableWithoutFeedback>
<View style={styles.exercise_name_container}>
<Text style={styles.exercise_name}>Side lunges</Text>
</View>
<TouchableWithoutFeedback
onPress={() => {
navigation.navigate('dashboard', { screen: 'dailychallange' });
}}>
<View style={styles.icons_container}>
<Image
style={styles.icon}
source={require('../../Assets/close.png')}
/>
</View>
</TouchableWithoutFeedback>
</View>
<View style={styles.bottom_container}>
<View style={styles.timer_container}>
<Text style={styles.timer_text}>02:47</Text>
</View>
{reps > 0 ? (
<View
style={[
styles.number_container,
{ justifyContent: speed > 0 ? 'space-between' : 'center' },
]}>
{speed > 0 ? <Text style={styles.number}>{speed} RS</Text> : null}
<Text style={styles.number}>{reps}/{exercises[ex].total}</Text>
</View>
) : null}
</View>
</Modal>
<WebView
ref={webviewRef}
mediaPlaybackRequiresUserAction={false}
source={{ uri: 'https://uactivsite-mobile.netlify.app/' }}
scalesPageToFit={false}
mixedContentMode="compatibility"
onMessage={onMessage}
onLoad={event => {
sendDataToWebView({
data: exercises[ex],
type: 'exercise'
})
}}
/>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1 },
preview: {
flex: 1,
},
top_container: {
zIndex: 1,
position: 'absolute',
top: 43,
paddingHorizontal: 20,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
},
bottom_container: {
zIndex: 1,
position: 'absolute',
bottom: 0,
width: '100%',
},
number: { color: 'white', fontSize: 28 },
exercise_name_container: {
height: 40,
width: 155,
alignItems: 'center',
justifyContent: 'center',
borderRadius: 20,
backgroundColor: 'rgba(255,255,255,0.2)',
},
number_container: {
height: 62,
backgroundColor: 'black',
width: '100%',
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 19,
},
timer_container: {
width: '100%',
height: 78,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
backgroundColor: 'rgba(255,255,255,0.45)',
alignItems: 'center',
},
timer_text: { color: 'black', fontSize: 48, fontFamily: 'Poppins-Bold' },
icons_container: {
height: 40,
width: 40,
alignItems: 'center',
justifyContent: 'center',
borderRadius: 50,
backgroundColor: 'rgba(255,255,255,0.8)',
},
icon: { width: 16, height: 16, resizeMode: 'contain' },
exercise_name: { color: 'black', fontFamily: 'Poppins-SemiBold', fontSize: 23 },
});
export default ExerciseCamera;
UPDATED CODE
This now fetches the data when button clicked but I cant display it as it did when it loaded first time?
I am totally new with react native so I am sorry if I explain my problem wrong or seem a little thick. I have made a screen that fetches data and displays the data and it seems to work quite well. However I have a couple of buttons and I want to add an onclick to call a function.
Maybe I am getting this wrong but its supposed to work like javascript which I have no problem with but I think I am missing something with the difference between components and functions.
For example in my code its just automatically fetches the data and displays it. How would I make functions to load the data when one the buttons is clicked and also update the view with the new loaded data?
I have tried putting the functions in with the fetch data but I seem to have to add everything but surely I can make re-usable functions for each task like I would in javascript.
I have included my code for the page and also what I have tried. Any help of advice would be great as when I am researching on the net I get confused information between reactjs and native.
Also all the code below has been snippets taken from various places and played around with so it is totally probably all wrong in terms of structure.
The code :
import React from "react";
import {
StyleSheet,
View,
ActivityIndicator,
FlatList,
Text,
StatusBar,
Image,
TouchableOpacity,
ScrollView,
SafeAreaView
} from "react-native";
import Icon from "react-native-vector-icons/Entypo";
import CupertinoButtonPurple1 from "../components/CupertinoButtonPurple1";
import Removebutton from "../components/removebutton";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
dataSource:[] };
}
componentDidMount(){
fetch("https://www.uberfantasies.com/testv2.php")
.then(response => response.json())
.then((responseData)=> {
this.setState({
loading: false,
dataSource: responseData.data
})
})
.catch(error=>console.log(error)) //to catch the errors if any
}
FlatListItemSeparator = () => {
return (
<View/>
);
}
renderItem=(data)=>
<SafeAreaView>
<View style={styles.container}>
<View style={styles.rect}>
<View style={styles.imageRow}>
<Image source={{uri: data.item.image}} style={styles.image} />
<View style={styles.group2Column}>
<View style={styles.group2}>
<Text style={styles.bitch}>
<Text>{data.item.from} sent you a mesage!</Text>
</Text>
<Text style={styles.loremIpsum}>
"{data.item.message}"
</Text>
</View>
<View style={styles.loremIpsum2Row}>
<Text style={styles.loremIpsum2}>{data.item.when}</Text>
<View style={styles.loremIpsum2Filler}></View>
<View style={styles.group3}>
<CupertinoButtonPurple1
style={styles.cupertinoButtonPurple1}
></CupertinoButtonPurple1>
<Removebutton
style={styles.removebutton}
></Removebutton>
</View>
</View>
</View>
</View>
</View>
</View>
</SafeAreaView>
render(){
if(this.state.loading){
return(
<View style={styles.loader}>
<ActivityIndicator size="large" color="#0c9"/>
</View>
)}
return(
<View style={styles.container}>
<FlatList
data= {this.state.dataSource}
ItemSeparatorComponent = {this.FlatListItemSeparator}
renderItem= {item=> this.renderItem(item)}
keyExtractor= {item=>item.id.toString()}
/>
</View>
)}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
loader:{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#fff"
},
rect: {
height: 97,
backgroundColor: "rgba(230,230, 230,0.57)",
borderWidth: 0,
borderColor: "rgba(0,0,0,0.57)",
marginTop: 0,
borderBottomWidth: 1,
borderBottomColor: "#d5d5d5",
backgroundColor: "#f4f4f4"
},
image: {
width: 80,
height: 80,
borderRadius: 15,
borderWidth: 4,
borderColor: '#ffffff',
shadowColor: '#d5d5d5',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.8,
shadowRadius: 2
},
group2: {
height: 41
},
bitch: {
fontFamily: "sans-serif-condensed",
color: "#121212",
fontSize: 14,
height: 17,
fontWeight: "bold",
marginTop: 2
},
loremIpsum: {
fontFamily: "sans-serif-condensed",
color: "#121212",
height: 17,
width: 159,
marginTop: 4
},
loremIpsum2: {
fontFamily: "sans-serif-condensed",
color: "#121212",
fontSize: 10,
marginTop: 8
},
loremIpsum2Filler: {
flex: 1,
flexDirection: "row"
},
group3: {
width: 125,
height: 26,
flexDirection: "row",
justifyContent: "space-between",
marginRight: 25
},
cupertinoButtonPurple1: {
height: 25,
width: 57
},
cupertinoButtonDanger2: {
height: 25,
width: 57
},
loremIpsum2Row: {
height: 26,
flexDirection: "row",
marginTop: 17,
marginRight: 33,
width: "100%"
},
group2Column: {
width: 275,
marginLeft: 16
},
imageRow: {
height: 84,
flexDirection: "row",
marginTop: 8,
marginLeft: 4
}
});
and the way I have tried to make a function and the way I think it should work with results?
The UPDATED code that fetches data but I cant get it to display data how it did in the previous code? Going out my nut here because I think Im missing something silly. If I had only one element to display or change I could do it but I think its because it looks through the results? Am i wrong?
Heres the code:
import React, { useState, Component } from 'react'
import {
StyleSheet,
View,
ActivityIndicator,
FlatList,
Text,
StatusBar,
Image,
TouchableOpacity,
ScrollView,
SafeAreaView
} from 'react-native'
class App extends Component {
state = {
loading: true,
dataSource:[],
Status: "Not loaded"
}
onPress = () => {
fetch("https://www.uberfantasies.com/testv.php")
.then(response => response.json())
.then((responseData)=> {
this.setState({
loading: false,
Status: "Loaded",
dataSource: responseData.data
})
console.log(this.state.dataSource)
})
.catch(error=>console.log(error)) //to catch the errors if any
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.button}
onPress={this.onPress}
>
<Text>Click me</Text>
</TouchableOpacity>
<View>
<Text>
Status : { this.state.Status }
</Text>
</View>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
button: {
alignItems: 'center',
backgroundColor: '#DDDDDD',
padding: 10,
marginBottom: 10
},
loader:{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#fff"
}
})
export default App;
if you want to call a function within onPress you need to call it. Like
<Button title="Press Me" onPress={() => ReloadData()} />
Below is a sample code snippet. I have used the function component as it is easy for a beginner. Assuming your output of the API call is a list, I am setting it to the state variable. SO whenever the 'apiData' variable value changes your component will re-render. You can user either a button or a touchable wrapper component for a clickable item.
const [apiData, setApiData] = useState([]);
const MySampleComponent = () => {
const apiCall = () => {
//Your code
setApiData(result);
};
const renderListItem = (itemData) => <Text>{itemData.item.<your key>}</Text>;
return (
<View>
<FlatList
keyExtractor={item => item.id}
data={apiData}
renderItem={renderListItem}
/>
<Button onPress={apiCall} />
<TouchableOpacity onPress={apiCall}>
<Text>Click Me</Text>
</TouchableOpacity>
</View>
);
};
Thanks for your comments and help, I think I have a much better way of understanding a few things now. I have managed to get a load button to fetch data and display it in the way as previously managed. I know it does not look like much progress but trust me this is a big step to getting to grips with the way things work for me.
Now time to play around with it, thanks again.
The working code (well best I can do at the minute!)
import React, { useState, Component } from 'react';
import {
StyleSheet,
View,
ActivityIndicator,
FlatList,
Text,
StatusBar,
Image,
TouchableOpacity,
ScrollView,
SafeAreaView,
} from 'react-native';
import Icon from 'react-native-vector-icons/Entypo';
import CupertinoButtonPurple1 from '../components/CupertinoButtonPurple1';
import CupertinoButtonDanger2 from '../components/CupertinoButtonDanger2';
class App extends Component {
state = {
loading: false,
dataSource: [],
Status: 'Not loaded',
};
componentDidMount() {
// this.onPress();
}
onPress = () => {
this.setState({
loading: true,
});
fetch('https://www.uberfantasies.com/testv.php')
.then((response) => response.json())
.then((responseData) => {
this.setState({
loading: false,
Status: 'Loaded',
dataSource: responseData.data,
});
console.log(this.state.dataSource);
})
.catch((error) => console.log(error)); //to catch the errors if any
};
FlatListItemSeparator = () => {
return <View />;
};
renderItem = (data) => (
<SafeAreaView>
<View style={styles.container}>
<View style={styles.rect}>
<View style={styles.imageRow}>
<Image source={{ uri: data.item.image }} style={styles.image} />
<View style={styles.group2Column}>
<View style={styles.group2}>
<Text style={styles.bitch}>
Bitch from Cov sent you a mesage!
</Text>
<Text style={styles.loremIpsum}>
"Hi there you Sexy Beast!"
</Text>
</View>
<View style={styles.loremIpsum2Row}>
<Text style={styles.loremIpsum2}>12 Feb 2022, 6.05 pm</Text>
<View style={styles.loremIpsum2Filler}></View>
<View style={styles.group3}>
<CupertinoButtonPurple1
style={
styles.cupertinoButtonPurple1
}></CupertinoButtonPurple1>
<CupertinoButtonDanger2
style={
styles.cupertinoButtonDanger2
}></CupertinoButtonDanger2>
</View>
</View>
</View>
</View>
</View>
</View>
</SafeAreaView>
);
render() {
if (this.state.loading) {
return (
<View style={styles.loader}>
<ActivityIndicator size="large" color="#0c9" />
</View>
);
}
return (
<View style={styles.container}>
<TouchableOpacity style={styles.button} onPress={this.onPress}>
<Text>Click me</Text>
</TouchableOpacity>
<View>
<Text>Status : {this.state.Status}</Text>
</View>
<FlatList
data={this.state.dataSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={(item) => this.renderItem(item)}
keyExtractor={(item) => item.id.toString()}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
button: {
alignItems: 'center',
backgroundColor: '#DDDDDD',
padding: 10,
marginBottom: 10,
},
loader: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
rect: {
height: 97,
backgroundColor: 'rgba(230,230, 230,0.57)',
borderWidth: 0,
borderColor: 'rgba(0,0,0,0.57)',
marginTop: 0,
borderBottomWidth: 1,
borderBottomColor: '#d5d5d5',
backgroundColor: '#f4f4f4',
},
image: {
width: 80,
height: 80,
borderRadius: 15,
borderWidth: 4,
borderColor: '#ffffff',
boxShadow: '0px 2px 4px 0px rgb(0 0 0 / 55%)',
},
group2: {
width: 275,
height: 41,
},
bitch: {
fontFamily: '-apple-system,Segoe UI,Roboto,sans-serif',
color: '#121212',
fontSize: 14,
height: 17,
fontWeight: 'bold',
marginTop: 2,
},
loremIpsum: {
fontFamily: '-apple-system,Segoe UI,Roboto,sans-serif',
color: '#121212',
height: 17,
width: 159,
marginTop: 4,
},
loremIpsum2: {
fontFamily: '-apple-system,Segoe UI,Roboto,sans-serif',
color: '#121212',
fontSize: 10,
marginTop: 8,
},
loremIpsum2Filler: {
flex: 1,
flexDirection: 'row',
},
group3: {
width: 121,
height: 26,
flexDirection: 'row',
justifyContent: 'space-between',
},
cupertinoButtonPurple1: {
height: 25,
width: 57,
},
cupertinoButtonDanger2: {
height: 25,
width: 57,
},
loremIpsum2Row: {
height: 26,
flexDirection: 'row',
marginTop: 17,
marginRight: 3,
},
group2Column: {
width: 275,
marginLeft: 16,
},
imageRow: {
height: 84,
flexDirection: 'row',
marginTop: 8,
marginLeft: 4,
},
});
export default App;
I am using react-native-bouncy-checkbox and 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.
However, I am trying to create a 'Select All' feature.
So, when user presses 'Select All' or taps on the 'checkbox' it should:
select all the items from the FlatList
add the total amount
allow user to edit all the selected amount individually
update the checkbox to show that it is selected.
So far I have tried getting all the 'checkbox' to show that it is selected when 'Select All' text is pressed or when 'checkbox' is pressed (beside select all text).
I have been trying to get this to work for the last couple of hours but couldn't manage to do it. So any help regarding this issue are most welcome.
CODE SNIPPETS AND APP SCREENSHOTS PROVIDED BELOW:
Code Sample:
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',
},
});
This is what it looks like currently. All checked items are individually selected:
This is what I am trying to achieve:
After checking the documentation of react-native-bouncy-checkbox, the following is important.
isCheckeddetermines the default internal checkbox state. It is only evaluated once.
The check-state handling is handled internally by the library.
We want to handle this on our own, in order to check all checkbox with a single state change. To do so, we need to set the disableBuiltInState prop to true "if you want to manually handle the isChecked prop and disable built in handling".
Thus, I would suggest the following workflow.
Create a state array for isChecked which contains a boolean flag for each of the elements.
Create a custom onPress-handler which gets the index from the FlatList as a parameter. With that index, we are able to to set the correct boolean flag in our state array to true or false.
If Select All is pressed, our onPress-handler sets all boolean flags of our state array to true. This will cause a rerendering of our screen and since we set disableBuiltInState prop to true, react-native-bouncy-checkbox will use our custom state handling.
Here is a minimal working example.
// 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>
)
This looks as follows.
Pressing Select All yields
Since you want to calculate additional values (total payable amount for each selected item) I would suggest to just add the checks state to your already implemented useEffect. When the checks state changes, this useEffect will be called. You can calculate all fields for which the boolean flag inside checks is true and set the state for the input fields.
Let refactor code as below
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",
},
});
Working example here - https://snack.expo.dev/#emmbyiringiro/971a0c
i need your helps.
I have a functional based component call WelcomeSlidePage. I want to pass a functional to the functional component. i try to console.log the function in functional component, in the first state, console.log is printing the function, but after the second state, it's becomes undefine.
this is my component that calling the functional component
WebViewPage.jsx
<Modal transparent={true} visible={this.state.WelcomeSlidePageModal} animationType="
<WelcomeSlidePage onDone={()=>{console.log('test bro');this.setState({WelcomeSlidePageModal:false})}}/>
</Modal>
and this is my functional component
WelcomeSlidePage.jsx
import React, { useState, useEffect } from 'react';
import { StyleSheet, View, Text, Image, TouchableOpacity } from 'react-native';
import AppIntroSlider from 'react-native-app-intro-slider';
import string from '../string'
import { _storeData, _getData } from '../components/StoreAsync';
export default WelcomeSlide = (props) => {
const [language, setLanguage] = useState('english');
const [showRealApp, setShowRealApp] = useState(false);
const welcomeSlide = string.welcome_slide[language]
useEffect(() => {
console.log('testing bro',props.onDone)
// getData();
});
async function getData() {
setLanguage( await _getData("language"));
setShowRealApp( await _getData("showRealApp"));
}
_renderItem = ({ item }) => {
switch (item.key) {
case ('k4'):
return (
<View style={{backgroundColor : '#a0c83a', flex: 1}}>
<Text style={styles.title}>{item.title}</Text>
<View style={{justifyContent: 'center', paddingHorizontal: 20, flex: 1}}>
<Text style={styles.text}>{item.text_4_a}</Text>
<View style={{flexDirection: 'row'}}>
<Image style={styles.icon} source={item.icon} />
<Text style={{paddingStart: 5, paddingEnd: 20, ...styles.text}}>{item.text_4_b}</Text>
</View>
<Text style={styles.text}>{item.text_4_c}</Text>
<View style={{flexDirection: 'row'}}>
<Image style={styles.icon} source={item.icon} />
<Text style={{paddingStart: 5, paddingEnd: 20, ...styles.text}}>{item.text_4_d}</Text>
</View>
</View>
</View>
);
case ('k5'):
return (
<View style={styles.slide}>
<Text style={styles.title}>{item.text_5}</Text>
<TouchableOpacity style={{marginVertical: 24}} onPress={()=>{ props.navigation.navigate('WebView', { url: string.onboarding[language].login_url }); }}>
<Text style={styles.button}>{item.text_5_btn1}</Text>
</TouchableOpacity>
<TouchableOpacity style={{marginVertical: 24}} onPress={()=>{ props.navigation.navigate('WebView', { url: string.onboarding[language].register_url }); }}>
<Text style={styles.button}>{item.text_5_btn2}</Text>
</TouchableOpacity>
</View>
);
default:
return (
<View style={styles.slide}>
<Text style={styles.title}>{item.title}</Text>
<Image style={styles.image} source={item.image} />
<Text style={{paddingHorizontal: 20, ...styles.text}}>{item.text}</Text>
</View>
);
}
}
_onDone = () => {
setShowRealApp(true);
_storeData('isPassSlide',true);
props.navigation.navigate('WebView', { url: string.onboarding[language].login_url,splashScreen:false });
}
const slides = [
{
key: 'k1',
title: welcomeSlide.title,
text: welcomeSlide.a,
image: require('../images/my_library_card_white_notext_nopadding.png'),
backgroundColor: '#a0c83a',
},
{
key: 'k2',
title: welcomeSlide.title,
text: welcomeSlide.b,
image: require('../images/my_account_white_notext_nopadding.png'),
backgroundColor: '#a0c83a',
},
{
key: 'k3',
title: welcomeSlide.title,
text: welcomeSlide.c,
image: require('../images/library_catalog_white_notext_nopadding.png'),
backgroundColor:'#a0c83a',
},
{
key: 'k4',
title: welcomeSlide.title,
text_4_a: welcomeSlide.d_a,
text_4_b: welcomeSlide.d_b,
text_4_c: welcomeSlide.d_c,
text_4_d: welcomeSlide.d_d,
icon: require('../images/icon-hand-right.png'),
backgroundColor: '#a0c83a',
},
{
key: 'k5',
text_5: welcomeSlide.e,
text_5_btn1: welcomeSlide.e_btn1,
text_5_btn2: welcomeSlide.e_btn2,
backgroundColor:'#a0c83a',
},
];
return(
<AppIntroSlider
renderItem={_renderItem}
prevLabel={string.back[language]}
nextLabel={string.next[language]}
doneLabel={string.next[language]}
showPrevButton={true}
slides={slides}
onDone={()=>props.onDone()}/>
)
}
const styles = StyleSheet.create({
slide : {
flex: 1,
paddingTop: (Platform.OS) === 'ios' ? 20 : 0,
paddingBottom: 80,
backgroundColor : '#a0c83a',
alignItems: 'center',
},
title: {
fontSize: 24,
color: '#fff',
fontWeight: 'bold',
textAlign: 'center',
marginTop : 30,
},
text: {
fontSize : 20,
color: '#fff',
textAlign: 'left',
},
image: {
width: 200,
height: 200,
resizeMode: 'contain',
flex:1,
},
icon: {
top: 10,
width: 25,
height: 15,
width: 25,
marginTop: -3,
},
content: {
paddingHorizontal: 20,
},
button: {
borderRadius: 8,
paddingVertical: 12,
paddingHorizontal: 12,
color: 'white',
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginHorizontal: 40,
backgroundColor: '#e46825',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.8,
shadowRadius: 2,
elevation: 5,
},
});
and then this is the console log
testing bro function onDone() {
console.log('test bro');
_this2.setState({
WelcomeSlidePageModal: false
});
} b11b05fa-4e76-41da-9d58-218edc178e45:157683:15
testing bro undefined
please help me guys, thanks
It seems to be as if I need to use some extra css in order to achieve what you will see below:
I already have this component:
renderTabBar = props => (
<View style={tabViewStyles.tabBar}>
{props.navigationState.routes.map((route, i) => {
return (
<TouchableOpacity
key={route.key}
style={[
tabViewStyles.tabItem,
tabViewStyles.tabStyle,
tabViewStyles[`tabStyle_${i}`],
]}
onPress={() => this.setState({ index: i })}
>
<Text style={{ color: '#ffffff', fontFamily: 'montserratBold' }}>
{route.title}
</Text>
</TouchableOpacity>
);
})}
</View>
);
With this css on StyleSheet:
container: {
flex: 1,
},
tabBar: {
flexDirection: 'row',
paddingTop: Constants.statusBarHeight,
},
onWhite: {
color: globalStyles.whiteColor.color,
backgroundColor: globalStyles.whiteColor.backgroundColor,
},
bolderFont: {
fontFamily: 'montserratBold',
},
tabItem: {
flex: 1,
alignItems: 'center',
padding: 26,
},
tabStyle: {
marginHorizontal: 10,
marginTop: 20,
borderRadius: 2,
},
tabStyle_0: {
backgroundColor: '#ff5252',
},
tabStyle_1: {
backgroundColor: '#3da7dc',
},
});
With that above I get this:
So I am still missing the pointy part of the tab.
What else do I need to do?
You can use rotate property of Transforms as described here.
Minimal example:
<View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
<View style={{width:50,height:50,backgroundColor:'green'}}></View>
<View style={{transform:[{rotateZ:'45deg'}],width:8,height:8,backgroundColor:'green',marginTop:-4}}></View>
</View>
Snack example here
If you want a pure styled solution and not an image you could do the following:
const triangle = {
width: 0,
height: 0,
backgroundColor: 'transparent',
borderStyle: 'solid',
borderLeftWidth: 50,
borderRightWidth: 50,
borderBottomWidth: 100,
borderLeftColor: 'transparent',
borderRightColor: 'transparent',
borderBottomColor: '#ff5252',
transform: [
{rotate: '180deg'}
]
}
const Triangle = React.createClass({
render: function() {
return (
<View style={[triangle, this.props.style]} />
)
}
})
Modified from https://codedaily.io/tutorials/22/The-Shapes-of-React-Native.