So, I'm building an application that monitors foreign exchange prices. It allows a user to input a price of a selected currency pair that they would like to be alerted once the inputted price is reached. I have tried using conditional rendering to do this but am only getting alerted on the same price that i have just put in the Textinput instead of monitoring the prices. Below is the code:
//Condition
const CheckAlertCondition = () => {
if( {...currency.data.prices[clickedindex].closeoutAsk} >= pricealert )
{
return(
Alert.alert("Target reached", "Price"+ pricealert+"has been hit")
)
}
else if({...currency.data.prices[clickedindex].closeoutBid} <= pricealert) {
return(
Alert.alert("Target reached", "Price"+ pricealert+"has been hit")
)
}
return false
}
<Modal
visible={modalopen}
animationType={"fade"}
>
<View style={styles.modal}>
<View>
<Text style={{textAlign: "center", fontWeight: "bold"}}>
{currency.data.prices[clickedindex].instrument}
</Text>
<Text style={{textAlign: "center"}}>
{currency.data.prices[clickedindex].closeoutAsk}/{currency.data.prices[clickedindex].closeoutBid}
</Text>
<Card.Divider/>
<View style={{ flexDirection: "row"}}>
<View style={styles.inputWrap}>
<TextInput
style={styles.textInputStyle}
value={pricealert}
onChangeText = {(pricealert) => setPricealert(pricealert)}
placeholder="Alert Price"
placeholderTextColor="#60605e"
numeric
keyboardType='decimal-pad'
/>
</View>
<View style={styles.inputWrap}>
</View>
</View>
<TouchableOpacity
onPress={() =>
ActionSheet.show(
{
options: BUTTONS,
cancelButtonIndex: CANCEL_INDEX,
destructiveButtonIndex: DESTRUCTIVE_INDEX,
title: "How do you want to receive your notification"
},
buttonIndex => {
setSheet({ clicked: BUTTONS[buttonIndex] });
}
)}
style={styles.button}
>
<Text>ActionSheet</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button}
onPress={() => {setModalOpen(false); CheckAlertCondition();showToastWithGravityAndOffset();} }>
<Text style={styles.buttonTitle}>OK</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
Related
I have a Sign Up screen in React Native, and when I press on the Input fields, my view does not go up therefore I can not see what I am typing. On other screens I have, the view gets pushed up. It feels like on Sign Up screen the "keyboardLayout" is set to "pan", but I did not specify it anywhere...
I am using Expo.
SignUp.js
return(
<View style={styles.container}>
<View style={styles.logoContainer}>
<Image source={logo} style={styles.logoStyle}/>
</View>
<View>
<Formik
initialValues={{email:'', fullname:'',password:'', password_repeat:'', adatkezeles:false}}
onSubmit={values =>{
onRegister_clicked(values.email,values.password,values.fullname)
}}
validationSchema={SignUpFormSchema}
validateOnMount={true}
>
{({handleChange, handleBlur, handleSubmit, values, isValid, errors, setFieldValue}) => (
<>
<View style={[styles.textBox,{
borderColor: values.email.length < 1 || emalValidate(values.email) ? color_theme_light.textBoxBorder : color_theme_light.loginTextInvalid
}]}>
<TextInput
placeholder='Email cím'
autoCapitalize='none'
keyboardType='email-address'
textContentType='emailAddress'
autoFocus={true}
caretHidden={false}
//Formik
onChangeText={handleChange('email')}
onBlur={handleBlur('email')}
value={values.email}
/>
</View>
<View style={[styles.textbox_information,{borderColor: 1 > values.password.length ||values.password.length >= 6 ? color_theme_light.textBoxBorder : color_theme_light.loginTextInvalid}]}>
<TextInput
placeholder='Jelszó'
autoCapitalize='none'
autoCorrect={false}
secureTextEntry={passwordVisible}
textContentType='password'
//Formik
onChangeText={handleChange('password')}
onBlur={handleBlur('password')}
value={values.password}
style={{width:(window.width)*0.75}}
/>
{passwordVisible ?
<TouchableOpacity onPress={onClick_setPasswordVisible}>
<Entypo name="eye-with-line" size={27} color={color_theme_light.iconColor} />
</TouchableOpacity>
:
<TouchableOpacity onPress={onClick_setPasswordVisible}>
<Entypo name="eye" size={27} color={color_theme_light.iconColor} />
</TouchableOpacity>
}
</View>
<View style={[styles.textbox_information,{borderColor:color_theme_light.textBoxBorder}]}>
<TextInput
placeholder='Jelszó megismétlése'
autoCapitalize='none'
autoCorrect={false}
secureTextEntry={passwordVisible}
textContentType='password'
//Formik
onChangeText={handleChange('password_repeat')}
onBlur={handleBlur('password_repeat')}
value={values.password_repeat}
style={{width:(window.width)*0.75}}
/>
</View>
{errors.password_repeat ?
<Text style={{color:'red', textAlign:'center', fontFamily:'QuicksandMedium'}}>{errors.password_repeat}</Text>
: null }
<View style={[styles.textBox,{
borderColor: 1 > values.fullname.length ||values.fullname.length >= 2 ? color_theme_light.textBoxBorder : color_theme_light.loginTextInvalid
}]}>
<TextInput
placeholder='Név'
autoCapitalize='words'
autoCorrect={false}
textContentType='name'
//Formik
onChangeText={handleChange('fullname')}
onBlur={handleBlur('fullname')}
value={values.fullname}
/>
</View>
<View style={{flexDirection:'row', alignItems:'center', justifyContent:'center',paddingTop:10}}>
<TouchableOpacity onPress={() => setFieldValue('adatkezeles', !values.adatkezeles)}>
{!values.adatkezeles ? <Ionicons name="square-outline" size={27} color={color_theme_light.iconColor} />
:
<Ionicons name="ios-checkbox" size={27} color={color_theme_light.iconColor} />
}
</TouchableOpacity>
<Text style={[fontStyles.baseText,{fontSize:13}]}>Elolvastam és elfogadom az </Text>
<TouchableOpacity onPress={() => setAdatkezelesModal(true)}>
<Text style={[fontStyles.baseText, {color:color_theme_light.loginLink, fontSize:13}]}>adatkezelési nyilatkozatot</Text>
</TouchableOpacity>
</View>
{adatkezelesModal ? <OpenAdatkezeles/> : null}
<View style={{alignItems:'center', justifyContent:'center', marginTop:20}}>
<TouchableOpacity onPress={handleSubmit}>
<View style={[styles.button(isValid),styles.shadow]}>
{!buttonLoading.loading ?
<Text style={fontStyles.loginButtonText}>Regisztráció</Text>
: <ButtonLoading/>}
</View>
</TouchableOpacity>
</View>
<View style={styles.signUpContainer}>
<Text style={fontStyles.baseText}>Már rendelkezel egy fiókkal?</Text>
<TouchableOpacity onPress={()=> navigation.navigate('LoginScreen')}>
<Text style={[fontStyles.baseText, {color:color_theme_light.loginLink}]}> Belépés</Text>
</TouchableOpacity>
</View>
</>
)}
</Formik>
</View>
</View>
)
}
you can either use KeyboardAvoidingView or make a custom keyboard avoiding view yourself.
also when using KeyboardAvoidingView dont forget to specify the behavior prop.
here is an example of custom keyboard avoiding view
import React, { useEffect, useRef } from 'react';
import { Keyboard, KeyboardEvent, Animated, Platform } from 'react-
native';
import { heightPercentageToDP as hp } from 'react-native-responsive-screen';
function CustomKeyboardAvoidingView(props: any) {
const heightAnim = useRef(new Animated.Value(hp('100%'))).current
function onKeyboardDidShow(e: KeyboardEvent) {
Animated.spring(heightAnim, {
toValue: hp('100%') - e.endCoordinates.height,
overshootClamping: true,
bounciness: 1,
speed: Platform.OS == 'ios'? 18 : 50,
useNativeDriver: false,
}).start()
}
function onKeyboardDidHide() {
Animated.spring(heightAnim, {
toValue: hp('100%'),
overshootClamping: true,
bounciness: 5,
speed: Platform.OS == 'ios'? 18 : 50,
useNativeDriver: false,
}).start()
}
useEffect(() => {
const SHOW_KEYBOARD_EVENT = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow'
const HIDE_KEYBOARD_EVENT = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide'
const showSubscription = Keyboard.addListener(SHOW_KEYBOARD_EVENT, onKeyboardDidShow);
const hideSubscription = Keyboard.addListener(HIDE_KEYBOARD_EVENT, onKeyboardDidHide);
return () => {
showSubscription.remove();
hideSubscription.remove();
};
}, []);
return (
<Animated.View style={[{
height: heightAnim,
}]} >
{props.children}
</Animated.View>
)
}
I managed to solve it with the following way:
<KeyboardAvoidingView>
<ScrollView>
{*Rest of the code*}
</ScrollView>
</KeyboardAvoidingView>
I honestly dont know why, but this moves up my view when I want to type.
I am implementing my own Modal, trying to replace the Alert.alert with something more beautiful. I made it to be displayed when needed, but it is not hiding on the button press, but I think I transferred it the needed function. My modal structure is the following:
export const RCModal = ({ title, visible, onButtonPress }) => {
return (
<Modal
animationType='fade'
transparent={true}
visible={visible}
>
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
<Pressable style={styles.button} onPress={onButtonPress}>
<Text style={styles.text}>OK</Text>
</Pressable>
</View>
</Modal>
)
};
And it is used in the application in the following way:
// ...
const [alertVisible, setAlertVisible] = useState(false);
const [alertTitle, setAlertTitle] = useState();
const [alertOnPress, setAlertOnPress] = useState();
// ...
const winner = (theWinner) => {
setBlocked(true);
setAlertTitle(`${theWinner} win!`);
setAlertOnPress(() => setAlertVisible(!alertVisible));
setAlertVisible(true);
}
// ...
return (
<View style={styles.container}>
<RCModal title={alertTitle} visible={alertVisible} onButtonPress={alertOnPress} />
<ScrollView contentContainerStyle={{ flexGrow: 1, justifyContent: 'center' }}>
<Text style={styles.title}>Noughts and Crosses</Text>
<Text style={styles.tip}>Get {winCondition()} in row, column or diagonal</Text>
<View style={styles.buttonsContainer}>
<Text style={styles.turnContainer}>Turn: <Text style={[styles.turn, { color: turn === 'X' ? '#2E86C1' : '#E74C3C'}]}>{turn}</Text></Text>
<TouchableHighlight underlayColor="#000000" style={[styles.button, styles.newGameButton]} onPress={setInitialFieldState}>
<Text style={styles.buttonText}>New game</Text>
</TouchableHighlight>
</View>
<Field state={fieldState} size={fieldSize} onCellPress={onCellPress} />
</ScrollView>
<View style={styles.settingsButtonContainer}>
<TouchableHighlight underlayColor={theme.colors.secondary} style={styles.settingsButton} onPress={onSettingsPress}>
<Image source={require('../img/settings.png')} style={styles.settingsIcon} />
</TouchableHighlight>
</View>
</View>
);
};
When the winner() is called, it is displayed as it should, but when I press OK button, it is not hiding. How can I fix it?
You can use setAlertVisible to change the alertVisible state:
<RCModal title={alertTitle} visible={alertVisible} onButtonPress={() => setAlertVisible(false)} />
The answer was that to set a function like a state variable, I needed to set it like
setAlertOnPress(() => () => setAlertVisible(false))
(2 x () =>)
I am using flatlist for show user input notes in my app. But Flat List is not render any item that i entered. I used static list for testing and it works correctly. but when i enter a text through textinput it is not showing.
const addToDoHandler = () => {
if(userInput == ''){
return;
}
const newTodo = {
id: Math.floor(Math.random() * 1000),
text: userInput,
completed: false,
};
const newTodos = [...todos, newTodo];
setTodos(newTodos);
//clear user input
setUserInput('');
};
const renderItem = ( {item} ) => {
<View style={{backgroundColor:'#6b4d87', marginVertical: 10, flexDirection:'row', alignItems:'center', justifyContent:'space-between', height:60, width:'95%', borderRadius:10 }}>
<View>
<Text style={{color:'black', fontSize:20, fontWeight:'bold'}}>{item.text}</Text>
</View>
<TouchableOpacity onPress={() => deleteHandler(item.id)}>
<TrashIcon size={35} name="trash" style={{color:'#eff9f0'}} />
</TouchableOpacity>
</View>
};
return (
<SafeAreaView style={styles.root}>
<View style={styles.txtInputView}>
<TextInput
value={userInput}
onChangeText={text => setUserInput(text)}
style={styles.textInput}
placeholder={"Enter Your Note..."}
/>
<TouchableOpacity style={styles.addButton} onPress={addToDoHandler}>
<PlusSign size={35} name="add-sharp" style={styles.plusIcon}/>
</TouchableOpacity>
</View>
<View style={{alignItems:'center'}}>
<FlatList
data={todos}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</View>
</SafeAreaView>
);
You need to return the components from renderItem.
Change it to:
const renderItem = ( {item} ) => (
<View style={{backgroundColor:'#6b4d87', marginVertical: 10, flexDirection:'row', alignItems:'center', justifyContent:'space-between', height:60, width:'95%', borderRadius:10 }}>
<View>
<Text style={{color:'black', fontSize:20, fontWeight:'bold'}}>{item.text}</Text>
</View>
<TouchableOpacity onPress={() => deleteHandler(item.id)}>
<TrashIcon size={35} name="trash" style={{color:'#eff9f0'}} />
</TouchableOpacity>
</View>
);
I saw some questions about it here on SO, but none of them work for me.
So my problem is when I click on the button "Add Friend" all of the buttons change to "remove", and I want to change only the button that I click
Normal
When I click in any button, all of them change to "remove"
My Code is:
export default class Friends extends Component {
state = {
visible: false,
userInfo: null,
requested: null
}
buttonAdd() {
if (this.state.requested === null) {
return (
<View style={styles.buttonContainer}>
<TouchableOpacity style={[styles.button, styles.add]} onPress={() => this.setState({ requested: true })}>
<Text style={{ color: "#fff", fontWeight: "bold" }}>Add Friend</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.button, styles.remove]}>
<Text style={{ color: "#050505", fontWeight: "bold" }}>Remove</Text>
</TouchableOpacity>
</View>
)
} else if (this.state.requested === true) {
return (
<View style={styles.buttonContainer}>
<TouchableOpacity style={[styles.button, styles.remove, styles.removeBig]} onPress={() => this.setState({ requested: false })} >
<Text style={{ color: "#050505", fontWeight: "bold" }}>Remove</Text>
</TouchableOpacity>
</View>
)
} else {
return (
<View style={styles.buttonContainer}>
<TouchableOpacity style={[styles.button, styles.add]} onPress={() => this.setState({ requested: true })}>
<Text style={{ color: "#fff", fontWeight: "bold" }}>Add Friend</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.button, styles.remove]}>
<Text style={{ color: "#050505", fontWeight: "bold" }}>Remove</Text>
</TouchableOpacity>
</View>
)
}
}
render() {
const inform = this.props.info
return (
<View>
<FlatList
data={inform}
keyExtractor={item => item.id.toString()}
ListHeaderComponent={this.topComponents()} //ignore this //with the ListHeaderComponent the components will not repeat
renderItem={({ item }) => (
<View style={styles.peopleContainer}>
<TouchableOpacity onPress={() => this.setState({ visible: true, userInfo: item })}> <Image source={item.photo} style={styles.photo} />
</TouchableOpacity>
<View style={styles.geralContent}>
<Text style={styles.name}>{item.name}</Text>
{this.buttonAdd()}
</View>
</View>
)}
/>
</View>
);
}
}
Sorry about the huge code.
There may be several approaches to handle this. This is just my approach to handle this specific issue.
Currently, all of the items sharing the same state. That means if any of the items update the component state, it will affect all the items of the FlatList. To overcome this problem we need to refactor the code so every item has its own state. Here is a snippet of how to do that and fix the problem
const FriendItem = ({ item, onPressPhoto }) => {
const [requested, setRequested] = React.useState(false)
return (
<View style={styles.peopleContainer}>
<TouchableOpacity onPress={() => onPressPhoto({ visible: true, userInfo: item })}>
<Image source={item.photo} style={styles.photo} />
</TouchableOpacity>
<View style={styles.geralContent}>
<Text style={styles.name}>{item.name}</Text>
<View style={styles.buttonContainer}>
{!requested ?
<TouchableOpacity style={[styles.button, styles.add]} onPress={() => setRequested(true)}>
<Text style={{ color: "#fff", fontWeight: "bold" }}>Add Friend</Text>
</TouchableOpacity>
:
null
}
<TouchableOpacity style={[styles.button, styles.remove]}>
<Text style={{ color: "#050505", fontWeight: "bold" }}>Remove</Text>
</TouchableOpacity>
</View>
</View>
</View>
)
}
export default class Friends extends Component {
state = {
visible: false,
userInfo: null,
}
onPressPhoto = ({ visible, userInfo }) => {
//write your logic here to handle on click
}
render() {
const inform = this.props.info
return (
<View>
<FlatList
data={inform}
keyExtractor={item => item.id.toString()}
ListHeaderComponent={this.topComponents()} //ignore this //with the ListHeaderComponent the components will not repeat
renderItem={({ item }) => <FriendItem item={item} onPressPhoto={this.onPressPhoto} />}
/>
</View>
);
}
}
I have a list of cards,
when pressed to any of them, I want to Increase hight for that card,
So I use layoutAnimation to handle this case because when I use Animated API from RN, I got an error when setNativeDrive "height"
Anyway,
In my case, it increases height for every card in the list,
So how can I solve it?
Code snippet
Live Demo
const OpenedAppointments = ({slideWidth}) => {
const [currentIndex, setCurrentIndex] = React.useState(null);
const [cardHeight, setCardHeight] = useState(140);
const collapseCard = (cardIndex) => {
setCurrentIndex(cardIndex);
currentIndex === cardIndex
? (LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut),
Animated.spring(),
setCardHeight(200))
: (LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut),
Animated.spring(),
setCardHeight(140));
};
const renderItems = (item, index) => {
return (
<TouchableOpacity
delayPressIn={0}
onPress={() => collapseCard(index)}
key={item.id}>
<Animated.View
style={[
styles.itemContainer,
{
height: cardHeight,
},
]}>
<View style={styles.headerContainer}>
<View style={styles.imgContainer}>
<Image
resizeMode="cover"
style={styles.img}
source={{uri: item.vendorInfo.vendorImg}}
/>
</View>
<View style={{width: '80%'}}>
<View style={styles.titleContainer}>
<Text numberOfLines={1} style={styles.vendorName}>
{item.vendorInfo.name}
</Text>
<View style={{flexDirection: 'row', alignItems: 'center'}}>
<Text style={styles.rateText}>{item.vendorInfo.rate}</Text>
<AntDesign name="star" size={18} color="#262626" />
</View>
</View>
<View style={styles.socialContainer}>
<View
style={{
width: 32,
height: 32,
backgroundColor: '#000',
borderRadius: 5,
}}
/>
</View>
</View>
</View>
<View style={styles.footer}>
<Text style={styles.serviceName}>
{item.serviceName}
</Text>
<Text style={styles.price}>
{item.price}
</Text>
</View>
// this will appeared when pressed to card "Footer Card"
<View>
<View style={styles.line} />
<View style={styles.editFooter}>
<View style={styles.dateContainer}>
<MemoCalender
style={styles.calenderIcon}
size={26}
color="#000"
/>
<View style={styles.dateBox}>
<Text style={styles.dateText}>{item.date}</Text>
<Text style={styles.dateText}>{item.time}</Text>
</View>
</View>
</View>
</View>
</Animated.View>
</TouchableOpacity>
);
};
return(
<>
{Data.opened.map((item, index) => renderItems(item, index))}
</>
);
}
Change this:
<Animated.View
style={[
styles.itemContainer,
{
height: cardHeight,
},
]
>
{...}
</Animated.View>
by
<Animated.View
style={[
styles.itemContainer,
{
height: currentIndex === index ? cardHeight : 140,
},
]
>
{...}
</Animated.View>
If you want to be more efficient, the default height of your card, define it in a constant (ex: const = DEFAULT_CARD_HEIGHT = 140) and use this constant wherever you use 140 as card height