react-native validation with useState and useCallback - react-native

I use useCallback validation for useState value, but that has get value delay.
This is my useState code
const [loading, setLoading] = useState(false);
const [amount, setAmount] = useState('');
const [rate, setRate] = useState('');
const [term, setTerm] = useState('');
const [type, setType] = useState('');
const [title, setTitle] = useState('');
const [canCalc, setCanCalc] = useState(false);
const [bText, setBtext] = useState(styles.defText);
const amountRef = useRef<TextInput | null>(null); //커서 직접이동
const rateRef = useRef<TextInput | null>(null);
const termRef = useRef<TextInput | null>(null);
const typRef = useRef<SelectDropdown | null>(null);
and useState uses inputtext
<TextInput
style={styles.textInput}
onChangeText={(text) =>onchangeAmount(text)}
value={amount}
placeholderTextColor="#666"
importantForAutofill="no"
keyboardType="decimal-pad"
clearButtonMode="while-editing"
returnKeyType="next"
ref={amountRef}
blurOnSubmit={false}
/>
All about useState TextInput same and different value amount, term...
And validation like this:
const onchangeAmount = useCallback(text => {
setAmount(text.trim());
checkValid(amount, rate, term, type,loading);
}, [checkValid]);
const onchangeRate = useCallback(text => {
setRate(text.trim());
checkValid(amount, rate, term, type,loading);
}, [checkValid]);
const onchangeTerm = useCallback(text => {
setTerm( text.trim());
checkValid(amount, rate, term, type);
}, [checkValid]);
const onChangeType = useCallback(text => {
setType(text);
checkValid(amount, rate, term, type,loading);
}, [checkValid]);
function checkValid (amount, rate, term, type,loading){
setLoading(true);
console.log( amount)
console.log( rate)
console.log( term)
console.log( type)
// if (loading) {
// return;
// }
console.log(loading)
//
// const valid=() =>{
// console.log( 'typeof ter========m')
console.log( amount)
console.log( rate)
console.log( term)
// console.log( type.type)
// console.log( type.type !=0)
console.log( !amount )
console.log( !amount && !amount.trim())
//
if (!amount || !amount.trim()) {
return Alert.alert('알림', '이메일을 입력해주세요.');
}
if(
!amount && !amount.trim() &&
!rate && !rate.trim() &&
!term && !term.trim() &&
Object.keys(type).length>0){
console.log("asdfasdfasdfasdf")
setCanCalc(true);
setBtext(styles.actText);
}else{
setCanCalc(false);
setBtext(styles.defText);
}
that validation for change style Pressable grey or blue
<Pressable
onPress={toComplate}
style={canCalc ?
styles.calcActive
:styles.buttonDef
}
disabled={!canCalc}
>
{loading?(
<ActivityIndicator color="white" />
):(
<Text
style={bText}>
calculation
</Text>
)
}
</Pressable>
Why delay get useState value?

Run the validation on an useEffect listening to amount change, that would simplify and make the flow more straight forward. Later on, use the result of the validation to set a different state variable, like the following:
useEffect(() => {
const isValid = runValidations(amount);
setIsValid(isValid)
}, [amount])

Related

Why when I enter 1999-10-17 it submits 1999-10-01 React native?

I'm trying to submit a form with a birthdate. But for one or another reason when I for example enter 1999-10-17 it submits 1999-10-01. It just always removes the last number and puts a 0 before. I've been trying everything I could imagine but nothing works...
Why this happens and how can I solve it???
const [birthdate, setBirthdate] = useState('');
const [yearB, setYearB] = useState('');
const [monthB, setMonthB] = useState('');
const [dayB, setDayB] = useState('');
const updateBirthdate = () => {
setBirthdate(`${yearB}-${monthB}-${dayB}`);
};
const handleYearBChange = (yearB) => {
setYearB(yearB);
updateBirthdate();
};
const handleMonthBChange = (monthB) => {
setMonthB(monthB);
updateBirthdate();
};
const handleDayBChange = (dayB) => {
setDayB(dayB);
updateBirthdate();
};
<TextInput
value={yearB}
onChangeText={handleYearBChange}
onSubmitEditing={() => yearBInput.current.blur()}
placeholder="year"
keyboardType="numeric"
/>
<TextInput
value={monthB}
onChangeText={handleMonthBChange}
onSubmitEditing={() => monthBInput.current.blur()}
placeholder="month"
keyboardType="numeric"
/>
<TextInput
value={dayB}
onChangeText={handleDayBChange}
onSubmitEditing={() => dayBInput.current.blur()}
placeholder="day"
keyboardType="numeric"
/>
You must use useEffect hook to update the birthdate with each rendering, follow the example:
const updateBirthdate = useCallback(() => {
setBirthdate(`${yearB}-${monthB}-${dayB}`);
}, [yearB, monthB, dayB]);
const handleYearBChange = (yearB) => {
setYearB(yearB);
};
const handleMonthBChange = (monthB) => {
setMonthB(monthB);
};
const handleDayBChange = (dayB) => {
setDayB(dayB);
};
useEffect(() => {
updateBirthdate()
}, [updateBirthdate])

Stripe Integration with React Native

I have to integrate Stripe with my React Native Application this phone application is a phone version of a desktop application where stripe is already being integrated and works fine so the backend is already been implemented. I wanted to use the context i used i nthe react js application however the useElementwas not available in stripe/stripe-react-native however after some reasearch i figured it didn't matter if i used the #stripe/stripe-js library my problem now is i keep getting the error: Could not find Elements context; You need to wrap the part of your app that calls useElements() in an <Elements> provider. However i already wrapped the form in an provider this is my checkoutForm.js:
import { useNavigation } from '#react-navigation/native';
import
React,
{ useState,
useEffect, useRef }
from 'react'
import {
View,
Text,
SafeAreaView,
StatusBar,
StyleSheet,
TouchableOpacity,
ScrollView,
Image,
Pressable ,
TextInput,
Alert}
from 'react-native';
import Icon from '../components'
import { COLORS } from '../constants';
import { useStateContext } from '../context/StateContext';
import { ProductCarousel } from '../../components';
import { useElements, Elements} from "#stripe/react-stripe-js"
import { CardField, useConfirmPayment, useStripe, } from '#stripe/stripe-react-native';
import { Button } from 'react-native-elements';
const CheckoutForm = () => {
const navigation = useNavigation();
const [isDeliveryAddressOpen, setIsDeliveryAddressOpen] = useState(false);
const [isContactNumberOpen, setIsContactNumberOpen] = useState(false);
const [isDeliveryInstructionsOpen, setIsDeliveryInstructionsOpen] = useState(false);
const [isCartItemsOpen, setIsCartItemsOpen] = useState(false);
const [isPaymentInfoOpen, setIsPaymentInfoOpen] = useState(false);
//const { confirmPayment, loading } = useConfirmPayment();
const [success, setSuccess ] = useState(false)
const stripe = useStripe()
const elements = useElements()
const cardElement = useRef(null);
const { totalPrice, cartItems } = useStateContext();
const [fullName, setFullName] = useState('');
const [email, setEmail] = useState('');
const [address, setAddress] = useState('');
const [address2, setAddress2] = useState('');
const [state, setState] = useState('');
const [city, setCity] = useState('');
const [zipCode, setZipCode] = useState('');
const [primaryNumber, setPrimaryNumber] = useState('');
const [SecondaryNumber, setSecondaryNumber] = useState('');
const [DeliveryInstructions, setDeliveryInstructions] = useState('');
const [isProcessing, setIsProcessing] = useState(false);
const [isError, setIsError] = useState(false);
//const [cardDetails, setCardDetails] = useState('')
const toggleDeliveryAddress = () => {
setIsDeliveryAddressOpen(!isDeliveryAddressOpen);
};
const toggleContactNumber = () => {
setIsContactNumberOpen(!isContactNumberOpen);
};
const toggleDeliveryInstructions = () => {
setIsDeliveryInstructionsOpen(!isDeliveryInstructionsOpen);
};
const toggleCartItems = () => {
setIsCartItemsOpen(!isCartItemsOpen);
};
const togglePaymentInfo = () => {
setIsPaymentInfoOpen(!isPaymentInfoOpen);
};
//const navigate = useNavigate();
const carts = {
status: 'pending',
items: (cartItems),
address,
fullName,
zipCode,
state,
city,
DeliveryInstructions,
primaryNumber,
SecondaryNumber,
totalPrice
}
const handleCheckout = async () => {
const response = await fetch('/create_cart', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(carts),
});
if(response.statusCode === 500) return;
// eslint-disable-next-line
const data = await response.json();
console.log('presseddddd', data)
}
/*========================================================*/
const handleSubmit = async () => {
const {error, paymentMethod} = await stripe.createPaymentMethod({
type: "card",
card: elements.getElement(CardField),
billing_details: {
name: fullName,
phone: primaryNumber,
email: email,
address: {
city: city,
line1: address,
state: state,
postal_code: zipCode
}
},
})
if(!error) {
try {
const {id} = paymentMethod
const carts = {
id,
amount: totalPrice,
confirm: true,
currency: 'CAD'
}
setIsProcessing(true);
const response = await fetch('/create-payment-intent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(carts),
});
if(response.status === 200) {
console.log(response.status)
console.log("Successful payment")
setSuccess(true)
}
const data = await response.json();
if(response.status >= 400) {
console.log(response.status)
console.log(data.error.split(':').pop())
setIsError(data.error)
}
} catch (error) {
console.log("Error", error)
}
} else {
console.log(error.message)
}
setIsProcessing(false)
//navigate('/');
}
return (
<View style={styles.container}>
<View style={styles.insideContainer}>
<TouchableOpacity style={styles.titleContainer} onPress={togglePaymentInfo}>
<Text style={styles.title}>Payment Info</Text>
{!isPaymentInfoOpen ?
<Icon icon ='add-outline' color='#000' size={20}/>
: <Icon icon ='remove-outline' color='#000' size={20}/>
}
</TouchableOpacity>
{isPaymentInfoOpen && (
<View style={styles.containercollapsed}>
<View style={{width: '98%'}}>
<CardField
ref={cardElement}
postalCodeEnabled={false}
placeholders={{
number: '4242 4242 4242 4242',
}}
cardStyle={{
backgroundColor: '#FFFFFF',
textColor: '#000000',
borderColor: COLORS.lightGray2,
borderWidth: 1,
borderRadius: 4
}}
style={{
width: '100%',
height: 50,
}}
onCardChange={(cardDetails) => {
}}
onFocus={(focusedField) => {
}}
/>
</View>
</View>
)}
</View>
<View style={styles.PaybuttonView}>
<Button
title="Pay"
onPress={ () => handleSubmit()}
/*disabled={loading}
loading={loading}*/
/>
{/*<Pressable style={styles.Paybutton} onPress={() => {handleCheckout(); handlePayPress()} }>
<Text style={{textAlign: 'center', fontSize: 20, color: '#FFFF', textTransform: 'uppercase'}}>Checkout</Text>
</Pressable>*/}
</View>
</View>
)
}
export default CheckoutForm
this is my StripeForm.js :
import React from 'react'
import {Elements} from "#stripe/react-stripe-js"
import CheckoutForm from './CheckoutForm';
import { loadStripe } from "#stripe/stripe-js"
const stripePromise = loadStripe(i removed the key but this is the key's place);
export const StipeForm = () => {
return (
<Elements stripe={stripePromise}>
<CheckoutForm />
</Elements>
)
}
To initialize Stripe to be used with the Stripe React Native SDK you need to use either StripeProvider or initStripe. See the docs here. You can't use the React Stripe.JS library initialize Stripe for the React Native SDK.

How to select one item of the array, when the user selects the contact?

What im trying to do is to select one or more phone numbers from the user contact list.
function ContactList() {
const [checked, setChecked] = useState(false);
const [contacts, setContacts] = useState([]);
const [filter, setFilter] = useState([]);
const [search, setSearch] = useState('');
const [data, setData] = useState(contacts)
useEffect(() => {
(async () => {
const { status } = await Contacts.requestPermissionsAsync();
if (status === 'granted') {
const { data } = await Contacts.getContactsAsync({
fields: [Contacts.Fields.PhoneNumbers],
});
if (data.length > 0) {
setContacts(data);
setFilter(data);
}
}
})();
}, []);
const searchFilter = (text) => {
if (text) {
const newData = contacts.filter((item) => {
const itemData = item.name ? item.name.toUpperCase() : ''.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
setFilter(newData);
setSearch(text);
} else {
setFilter(contacts);
setSearch(text);
}
};
const onChangeValue = () => {
setChecked(!checked)
};
useEffect(() => {
checked &&
setData((previous) => [...previous, {phone: contacts}])
}, [checked]
return(
<View>
<CheckBox
style={{ width: 15, height: 15 }}
right={true}
checked={checked}
onPress={onChangeValue}
/>
</View>
);
export default ContactList;
So far, when the user selects one phone number, it will select all phone numbers on his contact list.
I think I should get with the index only one contact from the list but I don't know how to get there.
How can I solve this error?
You can maintain a Map for checkboxes.
const [checked, setChecked] = useState(new Map());
on change use index to set values in Map.
const onChangeValue = (index) => {
checked.set(index, true)
};
in JSX use like this
<CheckBox
style={{ width: 15, height: 15 }}
right={true}
checked={checked.get(index)}
onPress={()=>onChangeValue(index)}
/>

if action.payload.number exist increase state.points with action.payload.points

In React-Native I´ve two TextInputs and an add-button
My add function works i.e it creates a list. But each time I fill out the form, it adds a new listpost. I want to check if action.payload.number exist, and if true, increase state.points with action.payload.points.
as yet everything I tried with have failed i.e it didn't recognize state.number === action.payload.number and adds a new row in the list
Please help me solving this issue
Thanks in advance
Pierre
const InputScreen = () => {
const [number, setNumber] = useState("");
const [points, setPoints] = useState("");
const {addScorer} = useContext(Context);
const onPress = () => {
addScorer(number, points);
setnumber("");
setPoints("");
};
return (
<View>
<Text style={styles.label}>enter Scorer Number:</Text>
<TextInput style={styles.input} value={number} onChangeText={setNumber} />
<Text style={styles.label}>enter Points Scored:</Text>
<TextInput style={styles.input} value={points} onChangeText={setPoints} />
<TouchableOpacity onPress={onPress}>
<FontAwesome5 name="plus-circle" size={44} color="coral" />
</TouchableOpacity>
</View>
);
};
export default InputScreen;
const scorerReducer = (state, action) => {
const id = Date.now().toString().slice(-4);
switch (action.type) {
case "add_scorer":
// if (state.number === action.payload.nummer) {
if (state.find((scorer) => scorer.id === action.payload.id)) {
return [
...state,
{
points: state.points + +action.payload.points,
},
];
} else {
return [
...state,
{
id: id,
number: action.payload.number,
points: +action.payload.points,
},
];
}
default:
return state;
}
};
const addScorer = (dispatch) => {
return (number, points, id) => {
dispatch({type: "add_scorer", payload: {number, points, id}});
};
};
export const {Context, Provider} = createDataContext(
scorerReducer,
{addScorer},
[]
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I solved it without using Reducer:)
export const ScoredProvider = ({children}) => {
const [playerList, setPlayerList] = useState([]);
const [number, setNumber] = useState("");
const [points, setPoints] = useState("");
const addScorer = () => {
const players = [...playerList];
if (number.trim().length === 0) {
return;
}
const posit = players.map((player) => player.number).indexOf(number);
if (posit !== -1) {
setPlayerList((playerList) =>
playerList.map((scorer, index) =>
index === posit
? {
...scorer,
points: scorer.points + +points,
}
: scorer
)
);
} else {
const newScorer = {
id: Date.now(),
number: number,
points: +points,
};
setPlayerList([...playerList, newScorer]);
setPoints(points);
}
};
return (
<ScoredContext.Provider
value={{number, setNumber, points, setPoints, playerList, addScorer}}
>
{children}
</ScoredContext.Provider>
);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

can not find the variable location with reversegeocodeAsync

Hello everyone who sees that question
I need help in that and full of hope that someone is gonna help
I am trying to get the exact location for the user to pass it finally in some other functionalities. I am using Expo init and expo-location
while using (reversegeocodeAsync({})) for the first render it's giving me the correct location but while testing it's crashing and giving an error and even works it's not making the data like after setting state it's not being available globally to use it
I tried different ways
First : use all the functions inside the same page but it doesn't work
import React, {useState, useEffect, useMemo} from 'react';
import {View, Text, StyleSheet, FlatList } from 'react-native';
import { NavigationEvents } from 'react-navigation';
import TimeApi from '../compnents/TimeApi';
import * as Location from 'expo-location';
const LocationScren = () => {
const [time, setsTime] = useState({});
const [errorMsg, setErrorMsg] = useState('');
const [location, setLocation ] = useState(null);
const [city, setCity ] = useState();
const getLocation = async () => {
let {status} = await Location.requestPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Access to Location denied');
}
const location = await Location.getCurrentPositionAsync({});
setLocation(location)
}
const getCity = async () => {
const place = await Location.reverseGeocodeAsync({
latitude : location.coords.latitude,
longitude : location.coords.longitude
});
place.find( p => {setCity(p.city);
})
}
const getTime = async () => {
const response = await TimeApi.get(`/${city}.json`);
setTime(response.data);
}
useEffect(() => {
getTime(), getLocation(), getCity();
} , []);
console.log(time);
console.log(location);
console.log(city);
return (
<View>
<FlatList
data = {time.items}
keyExtractor = {time => time.first}
renderItem = {({item}) => {
return (
<View>
<Text> {item.first} </Text>
<Text> {item.secnd} </Text>
<Text> {item.third} </Text>
<Text> {item.fourth} </Text>
<Text> {item.fifth} </Text>
<Text> {item.sixth} </Text>
</View>
);
}}
/>
{errorMsg ? <Text> {errorMsg} </Text> : null }
</View>
);
}
const styles = StyleSheet.create({});
export default LocationScren;
in here in the first render it's giving errors, then work , then giving that error ( null is not an object (evaluating 'location.coords')] )
Then I create a context file and added my functions and still getting the same error exactly
import createDataContext from './createDataContext';
import * as Location from 'expo-location';
const mwaqeetReducer = (state,action) => {
switch(action.type) {
case 'get_location' :
return action.payload;
case 'add_error' :
return {...state, errorMessage : action.error};
case 'get_city' :
return { cityName : action.payload};
default:
return state;
}
}
const getLocation = dispatch => async () => {
let {status} = await Location.requestPermissionsAsync();
if (status === !'granted') {
dispatch({type: 'add_error' , error : 'Permission to access location denied'});
}
let location = await Location.getCurrentPositionAsync({});
dispatch({type : 'get_location' , payload : location});
console.log(location);
}
const getCity = dispatch => async () => {
let keys = {
latitude : location.coords.latitude,
longitude : location.coords.longitude
}
const place = await Location.reverseGeocodeAsync(keys);
place.find( p => p.city);
dispatch({type : 'get_city' , payload : place});
console.log(place);
}
export const {Provider, Context} = createDataContext(
mwaqeetReducer, {
getLocation, getCity
} , {
errorMessage : '', location : {}, cityName : ''
}
)
so, please I need help to get over that.
You can try something like this.
useEffect(() => {
runFunction();
} , []);
const runFunction = async () => {
let {status} = await Location.requestPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Access to Location denied');
}
const location = await Location.getCurrentPositionAsync({});
setLocation(location)
const place = await Location.reverseGeocodeAsync({
latitude : location.coords.latitude,
longitude : location.coords.longitude
});
let city;
place.find( p => {
city = p.city
setCity(p.city)
});
const response = await TimeApi.get(`/${city}.json`);
setTime(response.data);
}