I'm running into the issue with my login screen where people autofill in their emails so it leaves a space at the end of the screen. I want to clear that when they move to add their password so was thinking of doing a onBlur that could probably use a .trim() or .replace() but how do I use that with my setEmail useState?
Screen blow:
const LoginScreen = ({ navigation }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const LogInUser = async() => {
if(email && password) {
try {
await auth.signInWithEmailAndPassword(email, password)
setEmail('')
setPassword('')
setError('')
navigation.replace('Home')
console.log('User Logged In = ');
} catch (err){
setError('Email or Password is wrong!');
console.log(err);
}
}
}
return (
<DismissKeyboard>
<View style={styles.screenStyle}>
<View style={{ ...StyleSheet.absoluteFill }}>
<Image source={require('../../assets/images/AccountBG.png')} style={{ flex: 1, height: null, width: null}} />
</View>
<StatusBar hidden={true} />
<View style={styles.loginBox}>
<Text style={[styles.whiteText, styles.headTextH1, styles.headingBufferBottom]}>Welcome</Text>
<Text style={styles.errorMessage}>{error}</Text>
<TextInput
style={styles.inputStyles}
placeholder="Email"
placeholderTextColor="#000"
value={email}
onChangeText={setEmail}
onBlur={cleanEmail}
/>
<TextInput
style={styles.inputStyles}
secureTextEntry
placeholder="Password"
placeholderTextColor="#000"
value={password}
onChangeText={setPassword}
/>
<OGButton title="Login" onPress={()=> LogInUser()} />
</View>
<View style={[styles.registerBox, styles.whiteText]}>
<Text style={styles.whiteText}>Don't have an account? | </Text>
<Text style={[styles.underlineText, styles.whiteText]} onPress={() => navigation.navigate('Register')} >Register Here</Text>
</View>
</View>
</DismissKeyboard>
);
}
You can trim the values and setEmail and whatever checks needed in onChangeText
onChangeText={(val) => {
// have other checks if needed here
setEmail(val.trim())
}
}
Related
I want to save the values from all input fields to getdata(), but I am getting undefined value
export default function Signupfor(props) {
// const phoneInput = useRef < PhoneInput > null;
const [text, setTextname] = useState();
function getdata() {
console.log('dsd');
console.log(text);
}
const {userInfo, log} = props?.route?.params;
console.log(log.name);
return (
<View style={styles.prheight}>
<View style={styles.form}>
<Text style={styles.r}>One Last Step</Text>
<TextInput
style={styles.forminput}
label="Name"
value={userInfo.user.name}
onChangeText={text => setTextname(text)}
/>
<TextInput
style={styles.forminput}
label="Email"
value={userInfo.user.email}
onChangeText={text => setTextemail(text)}
/>
<TextInput
style={styles.forminput}
label="Whatsapp Number"
keyboardType="numeric"
value={userInfo.user.number}
onChangeText={text => setTextnumber(text)}
// value={this.state.myNumber}
maxLength={10} //setting limit of input
/>
</View>
<View style={styles.buttonw}>
<Button color="#7743DB" title="Lets Go" onPress={() => getdata()} />
</View>
</View>
);
}
Here, name and email should not be able to be edited. I want to pass the value from value={userInfo.user.name} to the getdata()
you can use formik package for making form in react native
Installation
yarn add formik
Usage
import { Formik } from "formik";
export default function Signupfor(props) {
const { userInfo, log } = props?.route?.params;
console.log(log.name);
return (
<Formik
initialValues={{
name: userInfo.user.name,
email: userInfo.user.email,
number: userInfo.user.number,
}}
onSubmit={async (values, actions) => {
try {
console.log("name", values.name);
console.log("phone", values.number);
const params = {
full_name: values.name,
email: values.email,
phone_number: values.number,
};
} catch (error) {
let message = error.message;
console.log(message)
} finally {
actions.setSubmitting(false);
}
}}
>
{({
handleChange,
setFieldValue,
handleSubmit,
values,
errors,
touched,
isSubmitting,
}) => (
<View style={styles.prheight}>
<View style={styles.form}>
<Text style={styles.r}>One Last Step</Text>
<TextInput
style={styles.forminput}
label="Name"
value={values.name}
onChangeText={handleChange("name")}
/>
<TextInput
style={styles.forminput}
label="Email"
value={values.email}
onChangeText={handleChange("email")}
/>
<TextInput
style={styles.forminput}
label="Whatsapp Number"
keyboardType="numeric"
value={values.number}
onChangeText={handleChange("number")}
// value={this.state.myNumber}
maxLength={10} //setting limit of input
/>
</View>
<View style={styles.buttonw}>
<Button
color="#7743DB"
title="Lets Go"
onPress={() => handleSubmit()}
/>
</View>
</View>
)}
</Formik>
);
}
Your original method doesn't populate the state unless you edit the text input field, this is because your initialState doesn't have a value to start with. so firing getData() is reading empty state if the fields havent been changed.
onChangeText={text => setTextname(text)}
Only fire if you edit the textInput field.
Also I think you might be missing props, so first check if you are getting the correct data from props by logging it.
Once you have confirmed the props are available.
Set the initialState for name to userInfo.user.name
const { userInfo } = props?.route?.params;
const [name, setName] = useState(userInfo.user.name);
Then pass the state name to your TextInput and it should populate the value by reading from state.
return (
<>
<TextInput
placeholder="name"
value={name}
onChangeText={(text) => setName(text)}
/>
<Button title="Submit" onPress={() => getData()} />
</>
)
Make sure to create states for any additional values you wish to save.
const [name, setName] = useState(userInfo.user.name);
const [email, setEmail] = useState(userInfo.user.email);
You can use a library like https://react-hook-form.com to check an example with react native on video.
Or you can right it yourself, in the example below any time you need access to input values you can read it from text and number
const UselessTextInput = () => {
const [text, onChangeText] = useState("Useless Text");
const [number, onChangeNumber] = useState(null);
return (
<SafeAreaView>
<TextInput
style={styles.input}
onChangeText={onChangeText}
value={text}
/>
<TextInput
style={styles.input}
onChangeText={onChangeNumber}
value={number}
placeholder="useless placeholder"
keyboardType="numeric"
/>
</SafeAreaView>
);
};
You can do something like this!!
export default function Signupfor(props) {
const {userInfo, log} = props?.route?.params;
const [name, setName] = useState(userInfo?.user?.name);
const [phone, setPhone] = useState(userInfo?.user?.number);
function getdata() {
console.log("name",name)
console.log("phone",phone)
}
return (
<View style={styles.prheight}>
<View style={styles.form}>
<Text style={styles.r}>One Last Step</Text>
<TextInput
style={styles.forminput}
label="Name"
// this value must be same with useState
value={name}
onChangeText={text => setName(text)}
/>
<TextInput
style={styles.forminput}
label="Mobile"
value={phone}
onChangeText={text => setPhone(text)}
/>
</View>
<View style={styles.buttonw}>
<Button color="#7743DB" title="Lets Go" onPress={() => getdata()} />
</View>
</View>
);
}
Same goes for email.
In react native app, I have a home screen and a second screen that the user uses to add items that should be displayed on the home screen. I am using context to save the list of items. The problem is when I add items to the second screen and go to the home screen. The displayed list is empty.
Any help to explain why this happens and how to handle it? Here's the
Data Context
export const ExpenseContext = createContext();
App.js
const Stack = createNativeStackNavigator();
function App() {
const [expenseList, setExpenseList] = useState([]);
return (
<NavigationContainer>
<ExpenseContext.Provider value={{ expenseList, setExpenseList }}>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options={{ title: "Dashboard" }}
/>
<Stack.Screen
name="AddItem"
component={AddItem}
options={{ title: "CashFlowCreate" }}
/>
</Stack.Navigator>
</ExpenseContext.Provider>
</NavigationContainer>
);
}
export default App;
Home.js
function Home({ route, navigation }) {
const { expenseList } = useContext(ExpenseContext);
return (
<View style={styles.container}>
<Text style={styles.text}>Budget:</Text>
<Button title="+" onPress={() => navigation.navigate("AddItem")} />
<View>
<FlatList
style={styles.listContainer}
data={expenseList}
renderItem={(data) => <Text>{data.item.name}</Text>}
/>
</View>
</View>
);
}
export default Home;
AddItem.js
function AddItem({ navigation }) {
const { expenseList, setExpenseList } = useContext(ExpenseContext);
const [name, setName] = useState("");
const [amount, setAmount] = useState("");
const itemsList = expenseList;
return (
<View style={styles.container}>
<TextInput
style={styles.input}
onChangeText={(name) => setName( name )}
value={name}
placeholder="Name"
keyboardType="default"
/>
{name === "" && (
<Text style={{ color: "red", fontSize: 12, paddingLeft: 12 }}>
Name is required
</Text>
)}
<TextInput
style={styles.input}
onChangeText={(amount) => setAmount( amount )}
value={amount}
placeholder="Amount"
keyboardType="numeric"
/>
{amount === "" && (
<Text style={{ color: "red", fontSize: 12, paddingLeft: 12 }}>
Amount is required
</Text>
)}
<Button
title="Add"
style={styles.btn}
onPress={() => {
if (name === "" || amount === "") {
alert("Please Enter the required values.");
} else {
itemsList.push({
name: name,
amount: amount,
});
setExpenseList(itemsList);
}
}}
/>
<Button
title="View Dashboard"
style={styles.btn}
onPress={() => {
navigation.navigate("Home");
}}
/>
</View>
);
}
export default AddItem;
I solve it, in AddItem component remove const itemsList = expenseList; and onPress add button it should be like that instead
onPress={() => {
name === "" || amount === ""
? alert("Please Enter the required values.")
: setExpenseList([
...expenseList,
{
key:
Date.now().toString(36) +
Math.random().toString(36).substr(2),
name: name,
amount: amount,
},
]);
}}
I added the key because I needed later on.
There are several areas of issues in your code. One issue I can see is in AddItem. When you set:
const itemsList = expenseList
I think you did this for:
itemsList.push({
name: name,
amount: amount,
});
But you should look at the spread operator and try:
setExpenseList(...expenseList, {
name,
amount,
})
rewrite of AddItem.js:
function AddItem({ navigation }) {
const { expenseList, setExpenseList } = useContext(ExpenseContext)
const [name, setName] = useState('')
const [amount, setAmount] = useState('')
return (
<View style={styles.container}>
<TextInput style={styles.input} onChangeText={setName} value={name} placeholder='Name' keyboardType='default' />
{name === '' ? <Text style={styles.err}>Name is required</Text> : null}
<TextInput style={styles.input} onChangeText={setAmount} value={amount} placeholder='Amount' keyboardType='numeric' />
{amount === '' ? <Text style={styles.err}>Amount is required</Text> : null}
<Button
title='Add'
style={styles.btn}
onPress={() => {
name === '' || amount === ''
? alert('Please Enter the required values.')
: setExpenseList(...expenseList, {
name: name,
amount: amount,
})
}}
/>
<Button title='View Dashboard' style={styles.btn} onPress={() => navigation.navigate('Home')} />
</View>
)
}
export default AddItem
In your Home.js your FlatList it's missing the keyExtractor and you're trying to declare a prop of title outside of <Text>, rewrite:
function Home({ navigation }) {
const { expenseList } = useContext(ExpenseContext);
return (
<View style={styles.container}>
<Text style={styles.text}>Budget:</Text>
<Button title="+" onPress={() => navigation.navigate("AddItem")} />
<View>
<FlatList
style={styles.listContainer}
data={expenseList}
keyExtractor={(_,key) => key.toString()}
renderItem={(data) => <Text>{data.item.name}</Text>}
/>
</View>
</View>
);
}
export default Home;
Edit:
Answering to the comment. My understanding of the docs that is incorrect because keyExtractor is for identifying the id and by your commented code unless your passed in data to FlatList has a property of key then that wont work.
Also if key is not a string it should be:
keyExtractor={(item) => item.key.toString()}
I have created a sub component for updating values of recipes I fetch from API. However when I do so I get:
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of `UpdateCourse`.
Navigator to the Update function component:
<Button
title="UPDATE RECIPE"
style={Styles.searchBtn}
onPress={() =>
navigation.navigate("Update_course", {
id: "60c9a32cb076563888b5782a",
})
}
/>
</View>
Update Component:
import React, { useState, useEffect } from "react";
import { Button, Image, TextInput, Text, Linking, View } from "react-native";
import Styles from "../../styles/Styles";
import Alert from "../elements/Alert";
import Axios from "axios";
import APIRequest from "../elements/APIRequest";
export default function UpdateCourse(id) {
const [dishName, setdishName] = useState("");
const [category, setcategory] = useState("");
const [author, setauthor] = useState("");
const [ingredients, setingredients] = useState([]);
const [cookingTime, setcookingTime] = useState("");
const [sourceUrl, setsourceUrl] = useState("");
const [imageUrl, setimageUrl] = useState("");
const [isPublished, setisPublished] = useState("true");
const [price, setprice] = useState("");
const [tags, settags] = useState([]);
const [alert, setAlert] = useState("");
const url = `http://x.x.x.x:1234/api/courses/find/${id}`;
useEffect(() => {
async function getData() {
const result = await Axios.get(url);
setdishName(result.data.dishName);
setcategory(result.data.category);
setauthor(result.data.author);
setingredients(result.data.ingredients);
setcookingTime(result.data.cookingTime);
setsourceUrl(result.data.sourceUrl);
setimageUrl(result.data.imageUrl);
setisPublished(result.data.isPublished);
setprice(result.data.price);
settags(result.data.tags);
}
getData();
}, [url]);
async function update() {
let item = {
dishName,
category,
author,
ingredients,
cookingTime,
sourceUrl,
imageUrl,
isPublished,
price,
tags,
};
const result = await APIRequest(
`http://x.x.x.x:1234/api/courses/${id}`,
"PUT",
JSON.stringify(item)
);
if (result.status !== 200) {
return setAlert(
result.status +
" " +
result.statusText +
" - Please check TextInput fields"
);
}
}
const handleIngredientsChange = (event, index) => {
const shallowCopy = [...ingredients];
shallowCopy[index] = {
...shallowCopy[index],
[event.target.id]: event,
};
setingredients(shallowCopy);
};
return (
<View style={{ minWidth: 70 }}>
<View style={(Styles.bigBar, { textAlign: "center" })}>
<Text> Update recipe</Text>
</View>
<View
style={{
backgroundColor: "#f1f1f1",
padding: 3,
borderRadius: 1,
marginBottom: 7,
}}
>
<View style={{}}>
{alert !== "" && (
<View style={{ display: "flex", justifyContent: "center" }}>
<Alert alert={alert}></Alert>
</View>
)}
<View>
<Text>Name of the course</Text>
<TextInput
style={Styles.formControl}
value={dishName}
onChangeText={e => setdishName(e)}
/>
</View>
<View>
<Text>Category</Text>
<TextInput
style={Styles.formControl}
value={category}
onChangeText={e => setcategory(e)}
/>
</View>
<View>
<Text>Author</Text>
<TextInput
onChangeText={e => setauthor(e)}
style={Styles.formControl}
value={author}
/>
</View>
<View style={{ marginBottom: 2 }}>
<Text>Ingredients</Text>
</View>
{ingredients.map(({ quantity, unit, description }, index) => {
return (
<View style="ing" key={"key" + index}>
<Text>Quantity </Text>
<TextInput
key={"quantity" + index}
style={
(Styles.formControl,
{
marginLeft: 1,
marginRight: 1,
width: 5,
})
}
onChangeText={event => handleIngredientsChange(event, index)}
value={quantity}
/>
<View style={{ marginRight: 2 }}> Unit </View>
<View>
<TextInput
key={"unit" + index}
style={(Styles.formControl, { width: 5 })}
onChangeText={event =>
handleIngredientsChange(event, index)
}
value={unit}
/>
</View>
<View style={{ marginLeft: 1 }}>
{" "}
<Text>Description</Text>{" "}
</View>
<TextInput
key={"description" + index}
style={(Styles.formControl, { marginLeft: 1 })}
onChangeText={event => handleIngredientsChange(event, index)}
value={description}
/>
</View>
);
})}
<View>
<Text>Cooking time</Text>
<TextInput
onChangeText={e => setcookingTime(e)}
style={Styles.formControl}
value={cookingTime}
/>
</View>
<View>
<Text>Source url</Text>
<TextInput
onChangeText={e => setsourceUrl(e)}
style={Styles.formControl}
value={sourceUrl}
/>
</View>
<View>
<Text>Image url</Text>
<TextInput
onChangeText={e => setimageUrl(e)}
style={Styles.formControl}
value={imageUrl}
/>
</View>
<View>
<Text>Publish state</Text>
<TextInput
onChangeText={e => setisPublished(e)}
style={Styles.formControl}
placeholder={"default: true"}
/>
</View>
<View>
<Text>Price</Text>
<TextInput
onChangeText={e => setprice(e)}
style={Styles.formControl}
value={price}
/>
</View>
<View>
<Text>Tags</Text>
<TextInput
onChangeText={e => settags(e)}
style={Styles.formControl}
value={tags}
/>
</View>
<View style={(Styles.prettyB, { textAlign: "center" })}>
<Button onPress={update} title="Submit"></Button>
</View>
</View>
</View>
</View>
);
}
Sample data:
{"isPublished":true,"tags":["pizza","greek"],"_id":"60c9e360b076563888b578a2","dishName":"Greek pizza","category":"pizza","author":"Paolo","ingredients":[{"_id":"60c9e360b076563888b578a3","quantity":0.75,"unit":"cup","description":"warm water"},{"_id":"60c9e360b076563888b578a4","quantity":0.5,"unit":"cup","description":"bread flour"},{"_id":"60c9e360b076563888b578a5","quantity":1.5,"unit":"tea spoon","description":"sea salt"},{"_id":"60c9e360b076563888b578a6","quantity":1,"unit":"cup","description":"baby arugula"},{"_id":"60c9e360b076563888b578a7","quantity":0.67,"unit":"cup","description":"grape tomatoes halved"},{"_id":"60c9e360b076563888b578a8","quantity":1,"unit":"drizzle","description":"olive oil"},{"_id":"60c9e360b076563888b578a9","quantity":1.5,"unit":"tea spoon","description":"dry active yeast"},{"_id":"60c9e360b076563888b578aa","quantity":2,"unit":"tea spoon","description":"sugar"},{"_id":"60c9e360b076563888b578ab","quantity":1,"unit":"lb","description":"hummus any variety"},{"_id":"60c9e360b076563888b578ac","quantity":0.67,"unit":"cup","description":"good pitted greek olives"},{"_id":"60c9e360b076563888b578ad","quantity":0.25,"unit":"cup","description":"crumbled feta cheese"}],"cookingTime":75,"sourceUrl":"https://www.crete.pl/kuchnia-grecka-dania-glowne-wegetarianskie/grecka-pizza-z-feta.html","imageUrl":"https://www.crete.pl/zdjecia/grecka-pizza-z-feta/pizza1.jpg","price":35.99,"date":"2021-06-16T11:41:20.508Z","__v":0}
However it does work fine when I use . It then displays text I created. I do not understand why I cannot put Input tags on this subpage however. Thank you for all help!
You have to use the TextInput , there is no component with the name InputText or Input
https://reactnative.dev/docs/textinput
<Input style={Styles.searchInput} onChangeText={onChange} value={query} />
Should be
<TextInput style={Styles.searchInput} onChangeText={onChange} value={query} />
I am trying to change the background color of my react native app based on the current theme, i.e., whether it is dark or light.
Here is my code :
const SignUp = ({navigation}) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [name, setName] = useState("");
const [secureTextEntry, setSecureTextEntry] = useState(true);
const [error, setError] = useState("")
const clear = () => {
setEmail('')
setPassword('')
setName('')
setError('')
}
function onLinkClick(){
navigation.navigate('login')
clear()
}
async function onButtonPress() {
if(email && password && name){
setError("");
await auth().createUserWithEmailAndPassword(email, password)
.then(() => {
const {currentUser} = auth();
firestore().
collection(`users/${currentUser.uid}/name`)
.add({
name: name
})
clear()
navigation.navigate('login')
})
.catch(() => {
setError("Email already exists!!! Login instead.")
})
}else{
setError("Please fill all the fields");
}
}
var colour = '#1d2447'
const toggleSecureEntry = () => {
if(password){
setSecureTextEntry(!secureTextEntry);
}
};
const renderIcon = (props) => (
<TouchableWithoutFeedback onPress={toggleSecureEntry}>
<Icon {...props} name={secureTextEntry ? 'eye-off' : 'eye'}/>
</TouchableWithoutFeedback>
);
return(
<React.Fragment>
<View style={[styles.containerStyle, {backgroundColor: `${colour}`}]}>
<Text style={styles.textStyle} category='h2'>What should we call</Text>
<Text style={[styles.textStyle, {marginBottom: 10}]} category='h2'>you?</Text>
<Text style={styles.subtextStyle}>You'll need a name to make your</Text>
<Text style={styles.subtextStyle}>own posts, customize your blog,</Text>
<Text style={styles.subtextStyle}>and message people</Text>
<View style={styles.formStyle}>
<Input
style={styles.textInputStyle}
size='large'
placeholder='Email'
value={email}
onChangeText={nextValue => setEmail(nextValue)}
/>
<Input
value={password}
placeholder='Password'
accessoryRight={renderIcon}
secureTextEntry={secureTextEntry}
onChangeText={nextValue => setPassword(nextValue)}
style={styles.textInputStyle}
size="large"
autoCorrect
/>
<Input
value={name}
placeholder='Name'
onChangeText={nextValue => setName(nextValue)}
style={styles.textInputStyle}
size="large"
/>
</View>
<Button style={styles.button} size='medium' status='basic' appearance="outline" onPress={onButtonPress}>Sign Up</Button>
<Text style={styles.errorTextStyle} category='h6'>{error}</Text>
<Text style={styles.linkStyle}>Already have an account?</Text>
<Text style={styles.linkStyle} onPress={() => onLinkClick()}>Login</Text>
</View>
</React.Fragment>
)
Here I want to change the background depending on whether the current theme is dark or light, but I'm not able to fetch it.
I'm working on a messaging app with the following code:
const MessageCentral = () => {
const [inputText, onChangeText] = useState('');
const [bodyText, updateText] = useState("");
const addMessage = () => {
updateText(bodyText + inputText + "\n" );
}
const pressSend = () => {
addMessage();
}
return (
<SafeAreaView style= {StyleSheet.safeContainer}>
<View style={styles.container}>
<ScrollView
ref={scrollView =>this.scrollView = scrollView}
onContentSizeChange={scrollView.scrollToEnd({animated: true})}
style={styles.messageContainer}>
<Text>
{bodyText.toString()}
</Text>
</ScrollView>
<View style={styles.inputContainer}>
<TextInput
style={styles.messageInput}
onChangeText={text => onChangeText(text)}
value={inputText}
/>
<Button
style={styles.sendButton}
icon = { <Icon
name= "activity"
size={25}
color="blue"
/>
}
onPress={() => {
pressSend();
}}
/>
</View>
</View>
</SafeAreaView>
)
}
On the iOS emulator, when I enter some text without autocomplete and press the send button, the bodyText updates as it is supposed to. If I use autocomplete for the last word and press enter, bodyText is updated with the inputText before the autocomplete. How can I fix this?