React Native Dynamically created components not rendering on first click - javascript

I'm trying to create a list by dynamically adding the list items to an array in the state and then using the map operator to iterate over them. However, the new list items are only rendered after the second click on the button that handles the setState method. Any pointers on resolving this?
...
constructor(props) {
super(props);
this.state = {
requirements:[], // Placeholder array in state
currentRequirement
}
}
...
And in my render method I have this.
{
this.state.requirements.map((el,i) => (
<TouchableOpacity key={i}>
<BulletItem text={el}/>
</TouchableOpacity>
))
}
<FormInput
onChangeText={(value) => {
this.setState({ currentRequirement: value})}
}
placeholder="Enter a new requirement"
/>
<Button
title="Add Requirement"
onPress={() => {
this.onAddRequirementComponent()
}}
/>
The method for handling the setState is this.
onAddRequirementComponent() {
this.setState(previousState => ({
requirements: [...previousState.requirements, this.state.currentRequirement],
currentRequirement:''
}))
}
UPDATE : FULL COMPONENT
import React, { Component } from 'react';
import { Text, View, StyleSheet, ScrollView, Picker, TouchableOpacity } from 'react-native';
import { BulletItem, TagCloud } from "../components/index";
import { Actions } from "react-native-router-flux";
import {
Button,
Header,
FormInput,
FormLabel,
} from 'react-native-elements';
export default class ListScreen extends Component {
constructor(props) {
super(props);
this.state = {
jobtype: '',
level: '',
requirements: [],
benefits: [],
currentRequirement: '',
currentBenefit: ''
}
}
render() {
return (
<View style={styles.container}>
<Header
backgroundColor='#fff'
borderBottomWidth={0}
leftComponent={{ icon: 'corner-up-left', color: '#333', type: 'feather', onPress: () => { Actions.pop() } }}
centerComponent={{ text: 'Create New Job', fontFamily: 'VarelaRound-Regular', style: { color: '#333', fontSize: 18 } }}
rightComponent={{ icon: 'options', color: '#333', type: 'simple-line-icon' }} />
<ScrollView>
<FormLabel>Job Title</FormLabel>
<FormInput placeholder="e.g. Full Stack Javascript Developer"/>
<FormLabel >REQUIREMENTS</FormLabel>
{
this.state.requirements.map((el, i) => (
<TouchableOpacity key={i}><BulletItem containerStyle={{ backgroundColor: '#EFF0F2', borderRadius: 4 }} style={{ backgroundColor: '#EFF0F2' }} text={el} /></TouchableOpacity>
))
}
<FormInput
onChangeText={(value) => {
this.setState({ currentRequirement: value})}
}
placeholder="Enter a new requirement"
/>
<Button
title="Add Requirement"
onPress={() => {
this.onAddRequirementComponent()
}}
/>
<FormLabel style={{ fontFamily: 'VarelaRound-Regular', color: '#333' }} labelStyle={{ fontFamily: 'VarelaRound-Regular', color: '#333' }}>BENEFITS</FormLabel>
{
this.state.benefits.map((el, i) => (
<TouchableOpacity key={i}><BulletItem text={el} /></TouchableOpacity>
))
}
<FormInput value={this.state.currentBenefit} onChangeText={(value) => { this.setState({ currentBenefit: value }) }} placeholder="3 years experience developing Javascript apps" />
<Button title="Add" onPress={() => { this.onAddBenefitComponent() }}/>
<Picker selectedValue={this.state.jobtype}
style={{ height: 50, width: '100%', backgroundColor: '#EFF0F2' }}
onValueChange={(itemValue, itemIndex) => this.setState({ jobtype: itemValue })}>
<Picker.Item label="Full Time" value="fulltime" />
<Picker.Item label="Part Time" value="parttime" />
<Picker.Item label="Contract" value="contract" />
<Picker.Item label="Remote" value="remote" />
</Picker>
<Picker selectedValue={this.state.level}
style={{ height: 50, width: '100%', backgroundColor: '#EFF0F2' }}
onValueChange={(itemValue, itemIndex) => this.setState({ level: itemValue })}>
<Picker.Item label="Junior" value="junior" />
<Picker.Item label="Mid-Level" value="mid" />
<Picker.Item label="Management" value="management" />
<Picker.Item label="Senior" value="senior" />
</Picker>
</ScrollView>
</View>
);
}
onAddRequirementComponent() {
if (this.state.currentRequirement)
this.setState(previousState => ({
requirements: [...previousState.requirements, this.state.currentRequirement],
currentRequirement: ''
}))
}
onAddBenefitComponent() {
this.setState(previousState => ({
benefits: [...previousState.benefits, this.state.currentBenefit],
currentBenefit: ''
}))
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
}
});

This is the core logical part and works perfectly. I used only native elements like TextInput. The only change I made to make the TextInput to full controlled by adding value={this.state.currentRequirement} plus the suggestion of #Tholle for the usage of previousState.
class Test extends Component {
state = {
requirements: [],
currentRequirement: ''
}
onAddRequirementComponent() {
this.setState(previousState => ({
requirements: [...previousState.requirements, previousState.currentRequirement],
currentRequirement:''
}))
}
render() {
return(
<View>
<TextInput onChangeText={(value) => {
this.setState({ currentRequirement: value})}
}
value={this.state.currentRequirement}
/>
{ this.state.requirements.map((el,i) => (
<Text key={i}>{el}</Text>
))}
<Button
title="Add Requirement"
onPress={() => {
this.onAddRequirementComponent()
}}
/>
</View>
);
}
}

Related

React Context is not updating Data

I have created a React Context API, I have stored information such as first_name, hobbies, DOB etc in it. I have crated a state in the context file and a function that changes the state when called. I am using the context in SignUpForm1.js and SignUpForm2.js, I am trying to update the state of context variables when ever there is a change in the text field, but the data in the context is not updating.
UserData.js (Context)
import React from "react";
import { useState } from "react";
import {Text} from 'react-native';
import { createContext } from "react";
const SignUpContext = React.createContext({});
const UserData = (props)=>{
var state = {
pref_pronoun: "",
DOB: "",
course: "",
first_name: "",
hobby_1: "",
hobby_2: "",
hobby_3: "",
hobby_4: "",
hobby_5: "",
home_city: "",
last_name: "",
nationality: "",
student_email: "",
university: "",
}
const [userDetails , setDetails] = useState(state);
const updateFormData = (field, value) => {
setDetails({ [field]: value });
console.log(state);
};
return (
<SignUpContext.Provider value = {{state, updateFormData}}>
{props.children}
</SignUpContext.Provider>
)
}
export {SignUpContext, UserData} ;
SignUpForm1.js
import {
Image,
Text,
StyleSheet,
View,
StatusBar,
ScrollView,
RefreshControl,
} from "react-native";
import DropDown from "./DropDown";
import Input from "./Input";
import {
KeyboardAvoidingView,
TouchableWithoutFeedback,
Keyboard,
} from "react-native";
import { useCallback, useContext, useState } from "react";
import CustomButton from "./CustomButton";
import { useNavigation } from "#react-navigation/native";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import { Button } from "react-native";
import { SignUpContext, UserData } from "./GlobalUtil/UserData";
const HideKeyboard = ({ children }) => (
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
{children}
</TouchableWithoutFeedback>
);
function SignUpForm1() {
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
const s_context = useContext(SignUpContext);
const showDatePicker = () => {
setDatePickerVisibility(true);
};
const hideDatePicker = () => {
setDatePickerVisibility(false);
};
const navigation = useNavigation();
const NationalityData = ["Football", "Cricket", "Programmming", "Coding"];
const [refreshing, setRefreshing] = useState(false);
const onRefresh = useCallback(() => {
setRefreshing(true);
setTimeout(() => {
setRefreshing(false);
}, 2000);
}, []);
const statusBarHeight = Platform.OS === "ios" ? 50 : StatusBar.currentHeight;
return (
<KeyboardAvoidingView behavior="padding">
<HideKeyboard>
<View
style={{
height: "100%",
width: "100%",
backgroundColor: "#f1be63",
}}
>
<View
style={{ backgroundColor: "#f1be63", height: statusBarHeight }}
>
<StatusBar barStyle="dark-content" />
</View>
<ScrollView
contentContainerStyle={styles.rootContainer}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
/>
}
>
<Image
source={require("../assets/aeroplane.png")}
style={styles.image}
resizeMode="contain"
/>
<Text style={styles.header}>Let's get you set up</Text>
<Text style={styles.lowerHeader}>
(we promise that it won't take long)
</Text>
<View style={[styles.textFieldsContainer]}>
<View style={{ alignItems: "center" }}>
<Input
isLabel={true}
label="Preferred Pronoun"
placeholder="He/Him"
onChangeText={(text) => {
s_context.updateFormData("pref_pronoun", text);
}}
/>
<Input
isLabel={true}
label="First Name"
placeholder="Cameron"
onChangeText={(text) => {
s_context.updateFormData("first_name", text);
}}
/>
<Input
isLabel={true}
label="Last Name"
placeholder="Cox"
onChangeText={(text) => {
s_context.updateFormData("last_name", text);
}}
/>
<View
style={{
backgroundColor: "white",
width: "80%",
borderRadius: 5,
marginTop: 10,
}}
>
<Button
title="Date of Birth"
onPress={showDatePicker}
color="gray"
/>
</View>
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="date"
onConfirm={(date) => {
s_context.updateFormData('dob', date);
hideDatePicker();
}}
onCancel={hideDatePicker}
buttonTextColorIOS="white"
pickerContainerStyleIOS={{ backgroundColor: "#D89D35" }}
isDarkModeEnabled
/>
</View>
<View style={{ alignItems: "center" }}>
<DropDown
data={NationalityData}
placeholder="Nationality"
onSelect={(selectedItem, index) => {
s_context.updateFormData("nationality", selectedItem);
}}
/>
<DropDown
data={NationalityData}
placeholder="University"
onSelect={(selectedItem, index) => {
s_context.updateFormData("university", selectedItem);
}}
/>
<DropDown
data={NationalityData}
placeholder="Course"
onSelect={(selectedItem, index) => {
s_context.updateFormData("course", selectedItem);
}}
/>
<DropDown
data={NationalityData}
placeholder="HomeTown City"
onSelect={(selectedItem, index) => {
s_context.updateFormData("homeCity", selectedItem);
}}
/>
<CustomButton
isBorder={true}
title="Next"
onPress={() => {
navigation.navigate("SignUp2");
}}
/>
</View>
</View>
</ScrollView>
</View>
</HideKeyboard>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
rootContainer: {
height: "125%",
justifyContent: "flex-start",
alignItems: "center",
marginTop: 24,
},
textFieldsContainer: {
width: "100%",
flex: 1,
},
image: {
width: "25%",
height: "10%",
marginTop: 24,
},
header: {
color: "white",
fontSize: 26,
fontWeight: "bold",
marginVertical: 6,
},
lowerHeader: {
color: "white",
fontSize: 12,
marginBottom: 24,
},
});
export default SignUpForm1;
SignUpForm2.js
import {
View,
Text,
TouchableWithoutFeedback,
StyleSheet,
StatusBar,
ScrollView,
} from "react-native";
import { KeyboardAvoidingView } from "react-native";
import { Avatar } from "react-native-elements";
import Input from "./Input";
import DropDown from "./DropDown";
import { Keyboard } from "react-native";
import { Platform } from "react-native";
import { useNavigation } from "#react-navigation/native";
import CustomButton from "./CustomButton";
import { useState, useContext } from "react";
import { createNewUser } from "./util/auth";
import { SignUpContext, UserData } from "./GlobalUtil/UserData";
const HideKeyboard = ({ children }) => (
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
{children}
</TouchableWithoutFeedback>
);
function SignUpForm2() {
const s_context = useContext(SignUpContext);
async function FinishBtnHandler()
{
console.log("Creating New User");
console.log("Email: " + emailText.trim());
console.log("Password: " + passwordText.trim());
await createNewUser(emailText.trim(), passwordText.trim());
}
const navigation = useNavigation();
const hobbiesData = ["Football", "Cricket", "Programmming", "Coding"];
const [emailText, setEmailText] = useState("");
function handleEmailText(newText) {
console.log("Email: " + newText);
setEmailText(newText);
}
const [passwordText, setPasswordText] = useState("");
function handlePasswordText(newText) {
console.log("Password: " + newText);
setPasswordText(newText);
}
function avatarPressHandler() {
console.log("Pressed!");
}
const statusBarHeight = Platform.OS === "ios" ? 50 : StatusBar.currentHeight;
return (
<KeyboardAvoidingView behavior="padding" style={{ flex: 1 }}>
<HideKeyboard>
<ScrollView>
<View
style={{
position: "absolute",
top: 0,
left: 0,
right: 0,
zIndex: 999,
}}
>
<View
style={{
backgroundColor: "#f1be63",
height: statusBarHeight,
}}
>
<StatusBar barStyle="dark-content" />
</View>
</View>
<View style={[styles.rootContainer, { paddingBottom: 48 }]}>
<View style={styles.lowerContainer}>
<Text style={styles.primaryText}>You're almost there!</Text>
<Avatar
rounded
size={170}
containerStyle={{ alignSelf: "center", marginTop: 24 }}
//icon={{ name: "user", type: "font-awesome" }}
overlayContainerStyle={{ backgroundColor: "#f1be63" }}
source={{
uri: "https://cdn.pixabay.com/photo/2019/11/03/20/11/portrait-4599553__340.jpg",
}}
>
<Avatar.Accessory size={20} onPress={avatarPressHandler} />
</Avatar>
<Text
style={[
styles.secondaryText,
{ marginBottom: 8, marginTop: 16 },
]}
>
Express yourself & customize your avatar
</Text>
<Input
isLabel={true}
label="Student Email"
placeholder="cce22rnu#uea.ac.uk"
onChangeText={handleEmailText}
defaultValue={emailText}
/>
<Input
isLabel={true}
label="Create Password"
placeholder="iLoveyoushakila123"
onChangeText={handlePasswordText}
defaultValue={passwordText}
/>
<Input
isLabel={true}
label="Confirm Password"
placeholder="iLoveyoushakila123"
/>
<Text style={styles.secondaryText}>
Now the exciting part - select your top 5 hobbies
</Text>
<View style={{ alignItems: "center", marginTop: 16 }}>
<DropDown
data={hobbiesData}
onSelect={(selectedItem, index) => {
s_context.updateFormData("hobby_1", selectedItem);
}}
/>
<DropDown data={hobbiesData} onSelect={(selectedItem, index) => {
s_context.updateFormData("hobby_2", selectedItem);
}}/>
<DropDown data={hobbiesData} onSelect={(selectedItem, index) => {
s_context.updateFormData("hobby_3", selectedItem);
}}/>
<DropDown data={hobbiesData} onSelect={(selectedItem, index) => {
s_context.updateFormData("hobby_4", selectedItem);
}}/>
<DropDown data={hobbiesData} onSelect={(selectedItem, index) => {
s_context.updateFormData("hobby_5", selectedItem);
}}/>
<CustomButton
isBorder={true}
title="Finish"
/*onPress={()=>{navigation.navigate("ConfirmId")}}*/ onPress={
FinishBtnHandler
}
/>
</View>
</View>
</View>
</ScrollView>
</HideKeyboard>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
rootContainer: {
flex: 1,
justifyContent: "flex-start",
alignItems: "center",
backgroundColor: "#f1be63",
marginTop: 48,
backgroundColor: "#f1be63",
},
lowerContainer: {
flex: 1,
width: "100%",
alignItems: "center",
},
primaryText: {
color: "white",
fontSize: 24,
fontWeight: "bold",
},
secondaryText: {
marginTop: 8,
color: "white",
fontSize: 12,
fontWeight: "bold",
},
});
export default SignUpForm2;
You need to pass your userDetails state variable to the context, not the initial state value
<SignUpContext.Provider value={{ state: userDetails, updateFormData }}>
{props.children}
</SignUpContext.Provider>
Passing the initial state object will not see any updates you make.
Also, you should use the functional updates format to dynamically set properties in your state
setDetails((prev) => ({ ...prev, [field]: value }));
Finally, console logging state is a pointless exercise that often gives unexpected results. Just don't do it
The issue is that you are using state instead of userDetails in the updateFormData function. The state variable is never updated and remains the same initial value. To fix this, you should use setDetails instead of setState in the updateFormData function.
const updateFormData = (field, value) => {
setDetails({ ...userDetails, [field]: value });
console.log(userDetails);
};
Also, you are passing state in the SignUpContext.Provider value. You should pass userDetails instead of state.
return (
<SignUpContext.Provider value={{ userDetails, updateFormData }}>
{props.children}
</SignUpContext.Provider>
);

undefined is not an object (this.props.navigation.getParam)

FoodCreate.js
export class FoodCreate extends Component {
state = {
food: null,
foodList: [],
};
submitFood = (food) => {
this.setState({
foodList: [
...this.state.foodList,
{
key: Math.random(),
name: food,
},
],
});
this.props.navigation.navigate("FoodList", {
foodList: this.state.foodList,
deleteFood: this.deleteFood,
});
};
deleteFood = (key) => {
this.setState({
foodList: [...this.state.foodList.filter((item) => item.key != key)],
});
};
render() {
return (
<Container>
<Header>
<Left>
<Button transparent>
<Icon
name="arrow-back"
onPress={() => this.props.navigation.goBack()}
style={{ fontSize: 25, color: "red" }}
/>
</Button>
</Left>
<Body>
<Title>Add Food</Title>
</Body>
<Right>
<Button transparent>
<Icon
name="checkmark"
style={{ fontSize: 25, color: "red" }}
onPress={() => {
this.submitFood(this.state.food);
}}
/>
</Button>
</Right>
</Header>
<View style={{ alignItems: "center", top: hp("3%") }}>
<TextInput
placeholder="Food Name"
placeholderTextColor="white"
style={styles.inptFood}
value={this.state.food}
onChangeText={(food) => this.setState({ food })}
/>
</View>
</Container>
);
}
}
export default FoodCreate;
FoodList.js
export class FoodList extends Component {
constructor(props) {
super(props);
this.state = {
foodList: [],
};
}
render() {
return (
<Button onPress={() => this.props.navigation.navigate("FoodCreate")}>
Press to insert food
</Button>
<FlatList
data={this.props.navigation.getParam("foodList")} <-------
keyExtractor={(item, index) => item.key.toString()}
renderItem={(data) => <ListItem itemDivider title={data.item.name} />}
/>
);
}
}
export default FoodList;
Hey everyone, I'm building a Diet App, the food gets created in FoodCreate.js by typing a food and pressing the checkmark Icon, and this will create the food and display it in the FoodList.js, keep it in mind that the first Screen to be displayed is FoodList.js. When I run the code I get the following error: undefined is not an object (this.props.navigation.getParam)
try
<FlatList
data={props.route.params.foodList} <-------
...
...
/>
you must see document here: https://reactnavigation.org/docs/params/

React Native, values are not updating

I made a edit screen and trying to update the value of post through navigation v4 by using getParams and set setParams but when I change the old value and click save buttton to save it, it's not updating it and no error is showing either. It's still showing old values. Can someone please help me, below is my code
EditScreen.js
class EditScreen extends Component {
render() {
const { params } = this.props.navigation.state;
return (
<KeyboardAvoidingView
behavior="position"
keyboardVerticalOffset={Platform.OS === "ios" ? 0 : 100}
>
<Image
style={styles.image}
source={this.props.navigation.getParam("image")}
/>
<View style={styles.detailContainer}>
<AppTextInput
value={this.props.navigation.getParam("title")}
onChangeText={(text) =>
this.props.navigation.setParams({ title: text })
}
/>
<AppTextInput
value={this.props.navigation.getParam("des")}
onChangeText={(text) =>
this.props.navigation.setParams({ des: text })
}
/>
</View>
<AppButton
text="Save"
style={styles.button}
onPress={() => {
this.props.navigation.getParam("onEdit");
this.props.navigation.goBack();
}}
/>
</KeyboardAvoidingView>
Home.js
class Home extends Component {
state = {
modal: false,
post: [
{
key: "1",
title: "A Good Boi",
des: "He's a good boi and every one know it.",
image: require("../assets/dog.jpg"),
},
{
key: "2",
title: "John Cena",
des: "As you can see, You can't see me!",
image: require("../assets/cena.jpg"),
},
],
};
onEdit = (data) => {
const newPosts = this.state.post.map((item) => {
if (item.key === data.key) return data;
else return item;
});
this.setState({ post: newPosts, editMode: false });
};
render() {
return (
<Screen style={styles.screen}>
<FlatList
data={this.state.post}
renderItem={({ item }) => (
<>
<TouchableOpacity
activeOpacity={0.7}
onPress={() =>
this.props.navigation.navigate("Edit", {
image: item.image,
title: item.title,
des: item.des,
onEdit: this.onEdit,
})
}
style={styles.Edit}
>
<MaterialCommunityIcons
name="playlist-edit"
color="green"
size={35}
/>
</TouchableOpacity>
<Card onPress={() => this.props.navigation.push("Details", item)}>
<Image style={styles.image} source={item.image} />
<View style={styles.detailContainer}>
<Text style={styles.title} numberOfLines={1}>
{item.title}
</Text>
<Text style={styles.subTitle} numberOfLines={2}>
{item.des}
</Text>
</View>
</Card>
</>
I recommend you to keep the data in the component state:
constructor(props) {
super(props);
// get the data that you need from navigation params
const { key, title, ..} = this.props.navigation.state.params
this.state = {key, title, ..}
}
then :
<AppTextInput
value={this.state.title}
onChangeText={(text) =>
this.setState({ title: text })
}
/>
<AppButton
text="Save"
style={styles.button}
onPress={() => {
this.props.navigation.getParam("onEdit")(this.state);
this.props.navigation.goBack();
}}
/>
Maybe try this:
<AppButton
text="Save"
style={styles.button}
onPress={() => {
this.props.navigation.getParam("onEdit")(this.props.navigation.state.params);
this.props.navigation.goBack();
}}
/>
and:
this.props.navigation.navigate("Edit", {
key: item.key,
image: item.image,
title: item.title,
des: item.des,
onEdit: this.onEdit,
})

When putting new value into an input field erases a previous one

With my current code, I have two input fields and a drop down menu. When ever a value is placed into the field or modified, it clears the rest of the fields. The only one that will stay consistent is the drop down menu. I have suspicions that my useEffect hooks may be doing something, but I'm quite unsure of why. Any suggestions would be great.
(FYI: storeArtic is the push to firebase)
CustomScreen.js
import React, { useState } from "react";
import { StyleSheet, Text, Keyboard, View, TouchableWithoutFeedback } from "react-native";
import { Button, Input } from "react-native-elements";
import { Dropdown } from "react-native-material-dropdown";
import { storeArtic } from '../helpers/fb-settings';
const CustomScreen = ({ route, navigation }) =>{
//create a screen with the ability to add a picture with text to the deck of artic cards
//add check box solution for selection of word type (maybe bubbles, ask about this)
const articDrop = [
{value: 'CV'},
{value: 'VC'},
{value: 'VV'},
{value: 'VCV'},
{value: 'CVCV'},
{value: 'C1V1C1V2'},
{value: 'C1V1C2V2'},
];
const [articCard, setCard] = useState({
word: '',
imageUrl: '',
aType:'',
cType: '',
mastery: false
})
return(
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View>
<Text>Please enter the information of your custom card!</Text>
<Input
placeholder="Enter valid image url"
value={articCard.imageUrl}
autoCorrect={false}
onChangeText={(val) => setCard({ imageUrl: val })}
/>
<Input
placeholder="Enter word or phrase"
value={articCard.word}
autoCorrect={false}
onChangeText={(val) =>
setCard({ word: val, aType: val.charAt(0).toUpperCase(), mastery: false})
}
/>
<Dropdown
value={articCard.cType}
onChangeText={(text) => setCard({cType: text})}
label="Artic Type"
data={articDrop}
/>
<Button
//this will save the cards to the database
title="Save"
onPress={() => {
storeArtic({articCard})
}}
/>
<Button
title="Clear"
onPress={() => {
setCard({word: '', aType: '', cType: '', imageUrl: '', mastery: false});
navigation.navigate('Home');
}}
/>
</View>
</TouchableWithoutFeedback>
)
}
export default CustomScreen;
HomeScreen.js
import React, { useState, useEffect } from "react";
import { StyleSheet, Text, Keyboard, TouchableOpacity, View, TouchableWithoutFeedback, Image } from "react-native";
import { Button } from "react-native-elements";
import { Feather } from "#expo/vector-icons";
import { initArticDB, setupArticListener } from '../helpers/fb-settings';
const HomeScreen = ({route, navigation}) => {
const [ initialDeck, setInitialDeck] = useState([]);
useEffect(() => {
try {
initArticDB();
} catch (err) {
console.log(err);
}
setupArticListener((items) => {
setInitialDeck(items);
});
}, []);
useEffect(() => {
if(route.params?.articCard){
setCard({imageUrl: state.imageUrl, word: state.word, aType: state.aType, cType: state.cType, mastery: state.mastery})
}
}, [route.params?.articType] );
navigation.setOptions({
headerRight: () => (
<TouchableOpacity
onPress={() =>
navigation.navigate('Settings')
}
>
<Feather
style={styles.headerButton}
name="settings"
size={24}
color="#fff"
/>
</TouchableOpacity>
),
headerLeft: () => (
<TouchableOpacity
onPress={() =>
navigation.navigate('About')
}
>
<Text style={styles.headerButton}> About </Text>
</TouchableOpacity>
),
});
return(
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.container}>
<Text style={styles.textmenu}>Welcome to Artic Cards</Text>
<Text style={styles.textsubmenu}>Press Start to Begin!</Text>
<Image source={require('../assets/5-snowflake-png-image.png')}
style={{width: 300, height: 300, alignSelf: 'center'}}/>
<Button
title="Start"
style={styles.buttons}
onPress={() => navigation.navigate('Cards',
{passDeck: initialDeck})}
/>
<Button
title="Progress"
style={styles.buttons}
onPress={() => navigation.navigate('Progress')}
/>
<Button
title="Customize"
style={styles.buttons}
onPress={() => navigation.navigate('Customize')}
/>
</View>
</TouchableWithoutFeedback>
);
};
const styles = StyleSheet.create({
container: {
padding: 10,
backgroundColor: '#E8EAF6',
flex: 1,
justifyContent: 'center'
},
textmenu: {
textAlign: 'center',
fontSize: 30
},
textsubmenu:{
textAlign: 'center',
fontSize: 15
},
headerButton: {
color: '#fff',
fontWeight: 'bold',
margin: 10,
},
buttons: {
padding: 10,
},
inputError: {
color: 'red',
},
input: {
padding: 10,
},
resultsGrid: {
borderColor: '#000',
borderWidth: 1,
},
resultsRow: {
flexDirection: 'row',
borderColor: '#000',
borderBottomWidth: 1,
},
resultsLabelContainer: {
borderRightWidth: 1,
borderRightColor: '#000',
flex: 1,
},
resultsLabelText: {
fontWeight: 'bold',
fontSize: 20,
padding: 10,
},
resultsValueText: {
fontWeight: 'bold',
fontSize: 20,
flex: 1,
padding: 10,
},
});
export default HomeScreen;
Unlike class based setState, with functional components when you do setState, it will override the state with what you provide inside setState function. It is our responsibility to amend state (not overrite)
So, if your state is an object, use callback approach and spread previous state and then update new state.
Like this
<Input
placeholder="Enter valid image url"
value={articCard.imageUrl}
autoCorrect={false}
onChangeText={(val) => setCard(prev => ({ ...prev, imageUrl: val }))} //<----- like this
/>
Do the same for all your inputs.

ReactNative TextInput not letting me type

For both iOS and Android simulators
The text just disappears/flickers when I start typing. I tried having an initial state of texts with some value instead of keeping it empty. With this the TextInput sticks to this initial state and does not update itself with new text entered.
I think the state is not updating with 'onChangeText' property, but I am not completely sure.
People have seem to solve this, as they had few typos or missing pieces in code. However I have checked mine thoroughly.
Please help if I have missed anything in the below code.
LoginForm.js
import React, { Component } from 'react';
import { Card, Button, CardSection, Input } from './common';
class LoginForm extends Component {
state = { email: '', password: '' }
render() {
return (
<Card>
<CardSection>
<Input
label="Email"
placeHolder="user#gmail.com"
onChangeText={text => this.setState({ email: text })}
value={this.state.email}
/>
</CardSection>
<CardSection>
<Input
secureTextEntry
label="Password"
placeHolder="password"
onChangeText={text => this.setState({ password: text })}
value={this.state.password}
/>
</CardSection>
<CardSection>
<Button>
Log In
</Button>
</CardSection>
</Card>
);
}
}
export default LoginForm;
Input.js
import React from 'react';
import { TextInput, View, Text } from 'react-native';
const Input = ({ label, value, onChangeText, placeholder, secureTextEntry }) => {
const { inputStyle, labelStyle, containerStyle } = styles;
return (
<View style={containerStyle}>
<Text style={labelStyle}>{label}</Text>
<TextInput
secureTextEntry={secureTextEntry}
placeholder={placeholder}
autoCorrect={false}
style={inputStyle}
value={value}
onChangeText={onChangeText}
/>
</View>
);
};
const styles = {
inputStyle: {
color: '#000',
paddingRight: 5,
paddingLeft: 5,
fontSize: 18,
lineHeight: 23,
flex: 2
},
labelStyle: {
fontSize: 18,
paddingLeft: 20,
flex: 1
},
containerStyle: {
height: 40,
flex: 1,
flexDirection: 'row',
alignItems: 'center'
}
};
export { Input };
The only way to solve this was to change the way the values of TextInput fields are updated, with this code below.
value={this.state.email.value}
value={this.state.password.value}
You problem is how the Input component is written.
There is a render function written inside the stateless component which is not a React class component:
const Input = ({ label, value, onChangeText, placeHolder, secureTextEntry }) => ( // ← remove the wrapping parentheses
{
render() { // <--- this should not be here
↑
const { inputStyle, labelStyle, containerStyle } = styles;
return (
<View style={containerStyle} >
<Text style={labelStyle}>{label}</Text>
<TextInput
secureTextEntry={secureTextEntry}
autoCorrect={false}
placeholder={placeHolder}
style={inputStyle}
onChangeText={onChangeText}
value={value}
underlineColorAndroid="transparent"
/>
</View>
);
}
}
);
Change it to this:
const Input = ({ label, value, onChangeText, placeHolder, secureTextEntry }) => {
const { inputStyle, labelStyle, containerStyle } = styles;
return (
<View style={containerStyle} >
<Text style={labelStyle}>{label}</Text>
<TextInput
secureTextEntry={secureTextEntry}
autoCorrect={false}
placeholder={placeHolder}
style={inputStyle}
onChangeText={onChangeText}
value={value}
underlineColorAndroid="transparent"
/>
</View>
);
};
See running example

Categories

Resources