So I have this code here. I don't know if it's cause of how I structured it, but the state changes using the 'set' function doesn't change the state till after I change the entry again. So for example, I'll put in text then I push "post" which should update the state, when console.warn prints it doesn't print anything. Then I change the text again and push post and console.warn will output what was there before my most recent change.
import React, {useState, createRef} from 'react';
import LinearGradient from 'react-native-linear-gradient';
import {
StyleSheet,
TextInput,
View,
Text,
ScrollView,
Keyboard,
Button,
TouchableOpacity,
KeyboardAvoidingView,
} from 'react-native';
import DateTimePickerModal from 'react-native-modal-datetime-picker';
import Loader from '../components/Loader';
const GoalRegistrationScreen = ({navigation}) => {
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
const [goalText, setGoal] = useState('');
const [durationText, setDuration] = useState('');
const [descriptionText, setDescription] = useState('');
const [loading, setLoading] = useState(false);
const [errortext, setErrortext] = useState('');
const durationInputRef = createRef();
const descriptionInputRef = createRef();
function showDatePicker() {
setDatePickerVisibility(true);
}
function hideDatePicker() {
setDatePickerVisibility(false);
}
function handleConfirm(date) {
hideDatePicker();
console.warn(date);
}
function onButtonClick() {
// console.warn(goalText)
console.warn(descriptionText);
}
React.useLayoutEffect(() => {
navigation.setOptions({
title: 'Create Goal',
headerTitleAlign: 'center',
headerRight: () => (
<TouchableOpacity
style={styles.addButtonStyle}
activeOpacity={0.5}
onPress={onButtonClick}>
<LinearGradient
colors={['#FBE049', '#4964FB']}
style={styles.linearGradient}
start={{x: 0, y: 0}}
end={{x: 1, y: 1}}>
<Text style={styles.buttonTextStyle}>Post</Text>
</LinearGradient>
</TouchableOpacity>
),
});
}, [navigation]);
return (
<View style={styles.mainBody}>
<Loader loading={loading} />
<ScrollView keyboardShouldPersistTaps="handled">
<View>
<KeyboardAvoidingView enabled>
<View style={styles.SectionStyle}>
<TextInput
style={styles.inputStyle}
onChangeText={goal => setGoal(goal)}
placeholder="What do I want to accomplish?"
placeholderTextColor="#8b9cb5"
autoCapitalize="none"
returnKeyType="next"
onSubmitEditing={() =>
durationInputRef.current && durationInputRef.current.focus()
}
underlineColorAndroid="#f000"
blurOnSubmit={false}
/>
</View>
<View style={styles.SectionStyle}>
<Button title="Show Date Picker" onPress={showDatePicker} />
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="date"
onConfirm={handleConfirm}
onCancel={hideDatePicker}
minimumDate={new Date()}
/>
{/* <TextInput
style={styles.inputStyle}
onChangeText={duration => setDuration(duration)}
placeholder="Duration of Goal"
placeholderTextColor="#8b9cb5"
keyboardType="default"
ref={durationInputRef}
onSubmitEditing={() =>
descriptionInputRef.current &&
descriptionInputRef.current.focus()
}
blurOnSubmit={false}
underlineColorAndroid="#f000"
returnKeyType="next"
/> */}
</View>
<View style={styles.SectionStyle}>
<TextInput
style={styles.accomplishmentTextStyle}
onChangeText={description => setDescription(description)}
placeholder="How can I accomplish this goal?"
placeholderTextColor="#8b9cb5"
keyboardType="default"
ref={descriptionInputRef}
onSubmitEditing={Keyboard.dismiss}
blurOnSubmit={false}
underlineColorAndroid="#f000"
returnKeyType="next"
/>
</View>
{errortext != '' ? (
<Text style={styles.errorTextStyle}>{errortext}</Text>
) : null}
</KeyboardAvoidingView>
</View>
</ScrollView>
</View>
);
};
export default GoalRegistrationScreen;
const styles = StyleSheet.create({
mainBody: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#FFFFFF',
alignContent: 'center',
},
SectionStyle: {
flexDirection: 'row',
marginLeft: 35,
marginRight: 35,
margin: 10,
},
buttonTextStyle: {
color: '#000000',
paddingVertical: 10,
fontSize: 16,
paddingLeft: 15,
paddingRight: 15,
borderRadius: 32,
borderColor: '#000000',
},
inputStyle: {
flex: 1,
color: 'black',
paddingLeft: 15,
paddingRight: 15,
borderWidth: 1,
borderRadius: 30,
borderColor: '#000000',
},
accomplishmentTextStyle: {
flex: 1,
color: 'black',
paddingLeft: 15,
paddingRight: 15,
borderWidth: 1,
borderRadius: 30,
borderColor: '#000000',
height: 150,
textAlignVertical: 'top',
},
addButtonStyle: {
paddingRight: 20,
},
linearGradient: {
borderRadius: 32,
},
errorTextStyle: {
color: 'red',
textAlign: 'center',
fontSize: 14,
},
});
Here's an example I had 'testdsdd' in the description and updated to 'test', the onChangeText function should automatically have changed it to 'test', but it didn't. This happens for all states from goalText, descriptionText to setting date.
Try to change the TextInput onChangeText prop to just onChange like this:
onChangeText={ handleInputChange }
and then handle the new input in the handleInputChange:
const handleInputChange = useCallback((ev) => {
const input = ev.nativeEvent.text;
// you can also do error checking here.
setDescription(input);
}, [formatMessage]);
I always suggest using a pattern like this because it allows you to check for errors or call other methods (like auto-search).
In fact, I suggest using the handleInputChange to check for errors and only set the new state for the text-input on an onEndEditing.
Related
i got this problem when i try to save all date in firebase realTime Database pressing submit.
ERROR TypeError: undefined is not an object (evaluating '_app.default.initializeApp')
import firebase from 'firebase';
import React, { useState } from 'react';
import { View, Text, Modal, TouchableOpacity, FlatList, StyleSheet, DatePickerIOS, Image } from 'react-native';
import { Picker } from '#react-native-picker/picker';
const App = () => {
const [chosenDate, setChosenDate] = useState(new Date());
const [selectedValue, setSelectedValue] = useState('Alege Specializarea');
const [selectedDoctor, setSelectedDoctor] = useState('');
const [modalVisible, setModalVisible] = useState(false);
const [showPicker, setShowPicker] = useState(false);
const [showButtons, setShowButtons] = useState(false);
const options = ['Alege Specializarea', 'Cardiologie', 'Dermatologie', 'Oftalmologie', 'Analize Medicale'];
const optionMapping = {
'Alege Specializarea': [],
'Cardiologie': ['Dr. Alex', 'Dr. Balut'],
'Dermatologie': ['Dr. Chloe', 'Dr. David'],
'Oftalmologie': ['Dr. Emily', 'Dr. Frank'],
'Analize Medicale': ['Dr. Grace', 'Dr. Henry'],
};
const firebaseConfig = {
apiKey: "AIzaSyBhyYCXEaUlXqr83GtcbAfV2fiFooGzm2k",
authDomain: "first-app-7901e.firebaseapp.com",
databaseURL: "https://first-app-7901e-default-rtdb.europe-west1.firebasedatabase.app",
projectId: "first-app-7901e",
storageBucket: "first-app-7901e.appspot.com",
messagingSenderId: "876718439254",
appId: "1:876718439254:web:afe7784b89f066210334f7",
};
firebase.initializeApp(firebaseConfig);
const writeAppointmentData = (appointment) => {
firebase
.database()
.ref('appointments/' + appointment.id)
.set(appointment);
};
// Read data from the database
const readAppointmentData = (appointmentId) => {
return firebase
.database()
.ref('appointments/' + appointmentId)
.once('value')
.then((snapshot) => {
return snapshot.val();
});
};
const handleSubmit = () => {
const appointment = {
date: chosenDate,
specialization: selectedValue,
doctor: selectedDoctor,
};
writeAppointmentData(appointment);
setModalVisible(true);
};
const handleRead = () => {
readAppointmentData(appointmentId).then((appointment) => {
setAppointment(appointment);
});
};
return (
<>
<View style={styles.container}>
<Image source={require('./book.png')} style={styles.image} />
</View>
<View style={styles.container}>
<DatePickerIOS
date={chosenDate}
onDateChange={setChosenDate}
minuteInterval={30}
/>
<TouchableOpacity
style={styles.selectionButton}
onPress={() => setShowPicker(!showPicker)}
>
<Text style={styles.selectionButtonText}>{selectedValue}</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.submitButton}
onPress={handleSubmit}
>
<Text style={styles.submitButtonText}>Submit</Text>
</TouchableOpacity>
{showPicker && (
<Picker
selectedValue={selectedValue}
style={styles.picker}
onValueChange={(itemValue) => {
setSelectedValue(itemValue);
setShowButtons(itemValue !== 'Alege Specializarea');
}}
>
{options.map((option) => (
<Picker.Item
label={option}
value={option}
key={option}
/>
))}
</Picker>
)}
{showButtons && optionMapping[selectedValue].length > 0 && (
<View style={styles.buttonContainer}>
{optionMapping[selectedValue].map((buttonName) => (
<TouchableOpacity
style={[
styles.button,
buttonName === selectedDoctor && {
backgroundColor: '#24b6d4',
},
]}
key={buttonName}
onPress={() => setSelectedDoctor(buttonName)}
>
<Text
style={[
styles.buttonText,
buttonName === selectedDoctor && { color: 'white' },
]}
>
{buttonName}
</Text>
</TouchableOpacity>
))}
</View>
)}
</View>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Text style={styles.modalText}>
Your appointment has been scheduled for{' '}
{chosenDate.toString()} with {selectedDoctor}
</Text>
<TouchableOpacity
style={styles.modalButton}
onPress={() => setModalVisible(false)}
>
<Text style={styles.modalButtonText}>Close</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</>
);
};
const styles = StyleSheet.create({
container: {
top: -160,
flex: 1,
width: 350,
justifyContent: 'center',
},
image: {
alignSelf: 'center',
},
selectionButton: {
backgroundColor: 'lightgray',
padding: 10,
borderRadius: 10,
left: 20,
},
selectionButtonText: {
fontSize: 18,
},
picker: {
width: '100%',
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'center',
},
button: {
backgroundColor: '#fff',
borderWidth: 1,
borderColor: '#24b6d4',
borderRadius: 10,
padding: 10,
margin: 10,
width: 120,
},
buttonText: {
color: '#24b6d4',
fontSize: 18,
textAlign: 'center',
},
submitButton: {
backgroundColor: '#24b6d4',
borderRadius: 10,
padding: 10,
margin: 10,
},
submitButtonText: {
color: 'white',
fontSize: 18,
textAlign: 'center',
},
centeredView: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
marginTop: 22,
},
modalView: {
margin: 20,
backgroundColor: 'white',
borderRadius: 20,
padding: 35,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
},
modalText: {
marginBottom: 15,
textAlign: 'center',
},
modalButton: {
backgroundColor: '#24b6d4',
borderRadius: 10,
padding: 10,
margin: 10,
},
modalButtonText: {
color: 'white',
fontSize: 18,
textAlign: 'center',
},
});
export default App;
I use ExpoGO to "export" the project.
I tried many changes at firebase like: import firebase from 'firebase'; and all that changes for import firebase,none of that works but i tried with Firebase JS SDK and still dosent work.
I am trying to develop a mobile app with React Native using Firestore as a database. I'm trying to update a specific document when the "handleLike" function is called. I've passed the id of the posts through handleLike function. I'm trying to update the "comment" field for the sake of the trial. The problem is it updates all of the documents in the database. Also handleLike function is called without me pressing the like button whenever I refresh the page. I get that it is in a loop but I couldn't figure out what I'm doing wrong. I would very much appreciate your help. I have added screenshots of the database and the console.
import { React, useState, useEffect } from "react";
import {
View,
Text,
StyleSheet,
Image,
FlatList,
TouchableOpacity,
} from "react-native";
import { Ionicons, MaterialIcons } from "#expo/vector-icons";
import { useNavigation } from "#react-navigation/core";
import { auth, storage, firestore,firebase } from "../firebase";
import { preventAutoHideAsync } from "expo-splash-screen";
import moment from 'moment';
function Feed() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [userData, setUserData] = useState(null);
//const [deleted, setDeleted] = useState(false);
const fetchPosts = async () => {
try {
const list = [];
const profileRef = firestore.collection("users").doc(auth.currentUser.uid);
const doc = await profileRef.get();
await firestore
.collection("posts")
.orderBy('postTime', 'desc')
.get()
.then((querySnapshot) => {
console.log("Total Posts: ", querySnapshot.size);
querySnapshot.forEach((doc) => {
const pId = doc.id
const {
userId,
userName,
userSurname,
post,
postImg,
postTime,
userImg,
likes,
comments,
likeByUsers } = doc.data();
list.push({
id: pId,
userId,
userName,
userSurname,
userImg:userImg,
post,
postImg,
postTime: postTime,
liked: false,
likes,
comments,
likeByUsers,
});
});
});
setPosts(list);
if (loading) {
setLoading(false);
}
console.log("Posts: ", posts);
} catch (e) {
console.log(e);
}
};
useEffect(() => {
fetchPosts();
}, []);
const renderPost = (posts) => {
const handleLike = (id) => {
console.log(id)
firestore.collection('posts')
.doc(id)
.update({
comment: "tryout 2"
})
}
return (
<View style={styles.feedItem}>
<Image source={{uri: posts.userImg}} style={styles.avatar} />
<View style={{ flex: 1 }}>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
}}
>
<View>
<Text style={styles.name}>{posts.userName} {posts.userSurname}</Text>
<Text style={styles.timestamp}>{moment(posts.postTime.toDate()).fromNow()}</Text>
</View>
<MaterialIcons name="expand-more" size={24} color="#73788B" />
</View>
<Text style={styles.post}>{posts.id}</Text>
<Image
source={{uri: posts.postImg}}
style={styles.postImage}
resizeMode="cover"
/>
<View style={{ flexDirection: "row" }}>
<TouchableOpacity onPress={handleLike(posts.id)}>
<Ionicons
name="heart-outline"
size={24}
color="#73788B"
style={{ marginRight: 16 }}
/>
</TouchableOpacity>
<Ionicons name="chatbox-outline" size={24} color="#73788B" />
</View>
</View>
</View>
);
};
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>Feed</Text>
<TouchableOpacity style={styles.addButton}>
<Ionicons name="add-circle" size={32} color="black" />
</TouchableOpacity>
</View>
<FlatList
style={styles.feed}
data={posts}
renderItem={({ item }) => renderPost(item)}
keyExtractor={(item) => item.id}
showsVerticalScrollIndicator={false}
></FlatList>
</View>
);
}
const styles = StyleSheet.create({
addButton: {
position: "absolute",
right: 30,
top: 60,
},
container: {
flex: 1,
backgroundColor: "#EBECF4",
},
header: {
paddingTop: 64,
paddingBottom: 16,
backgroundColor: "#FFF",
alignItems: "center",
justifyContent: "center",
borderBottomWidth: 1,
borderBottomColor: "#EBECF4",
shadowColor: "#454D65",
shadowOffset: { height: 5 },
shadowRadius: 15,
shadowOpacity: 0.2,
zIndex: 10,
},
headerTitle: {
fontSize: 20,
fontWeight: "500",
},
feed: {
marginHorizontal: 16,
},
feedItem: {
backgroundColor: "#FFF",
borderRadius: 5,
padding: 8,
flexDirection: "row",
marginVertical: 8,
},
avatar: {
width: 36,
height: 36,
borderRadius: 18,
marginRight: 16,
},
name: {
fontSize: 15,
fontWeight: "500",
color: "#454D65",
},
surname: {
fontSize: 15,
fontWeight: "500",
color: "#454D65",
},
timestamp: {
fontSize: 12,
color: "#808588",
marginTop: 4,
},
post: {
marginTop: 16,
fontSize: 14,
color: "#838899",
},
postImage: {
width: 267,
height: 150,
borderRadius: 5,
marginVertical: 16,
},
});
export default Feed;
Console Screenshot:
Firestore Database Screenshot:
I'm building an app in React Native where a user can plan out novels by creating and storing characters, locations, and chapters, with details about them. Currently, there is an issue where navigating to the screen where one can edit a character's details (CharEdit.js below), from a screen that displays those details in a better format (CharInfo.js), introduces the following warning:
Warning: Cannot update a component from inside the function body of a different component
CharEdit.js
import React from 'react';
import { ScrollView, Text, TextInput, View, TouchableOpacity, StyleSheet } from 'react-native';
import { Picker } from '#react-native-picker/picker';
import { useSelector, useDispatch } from 'react-redux';
import { updateCharacter } from '../redux/actions';
import { names, descriptions, avatars } from '../assets/images';
export default function charEditScreen({ navigation, route }) {
const { id } = route.params;
let character = useSelector(state => state.metaReducer.characters.filter((item) => {if (item.id === id) return item;}))[0];
let nameValue;
let ageValue;
let professionValue;
let appearanceValue;
let motiValue;
let summaryValue;
let role;
let pictureValue = names.filter((key) => {
return avatars[key] === character.picture;
})[0];
if (pictureValue === undefined) {
pictureValue = 'icon';
}
try {
role = character.role;
} catch {
role = '';
}
const dispatch = useDispatch();
const charUpdate = () => {
let changedCharacter = {
name: (nameValue) ? nameValue : character.name,
age: (ageValue) ? ageValue : character.age,
profession: (professionValue) ? professionValue : character.profession,
appearance: (appearanceValue) ? appearanceValue : character.appearance,
picture: avatars[pictureValue],
motivation: (motiValue) ? motiValue : character.motivation,
summary: (summaryValue) ? summaryValue : character.summary,
role: role,
id: id,
};
let action = updateCharacter(changedCharacter);
dispatch(action);
navigation.navigate('CharacterList');
}
return(
<ScrollView contentContainerStyle={{alignItems: 'center', justifyContent: 'center', flexGrow: 1, backgroundColor: '#F0F2F2'}}>
<Text style={styles.header}>{character.name}</Text>
<View style={styles.fieldContainer}>
<Text style={styles.fieldLabel}>Full Name</Text>
<TextInput value={nameValue} onChangeText={(change) => {nameValue = change}} style={styles.textInput} autoCapitalize='words' defaultValue={character.name} />
</View>
<View style={styles.fieldContainer}>
<Text style={styles.fieldLabel}>Age</Text>
<TextInput value={ageValue} onChangeText={(change) => {ageValue = change;}} style={styles.textInput} defaultValue={character.age} />
</View>
<View style={styles.fieldContainer}>
<Text style={styles.fieldLabel}>Profession/Occupation</Text>
<TextInput value={professionValue} onChangeText={(change) => {professionValue = change;}} style={styles.textInput} defaultValue={character.profession} />
</View>
<View style={styles.fieldContainer}>
<Text style={styles.fieldLabel}>Physical Appearance</Text>
<TextInput value={appearanceValue} onChangeText={(change) => {appearanceValue = change;}} style={styles.textInputMultiline} multiline={true} defaultValue={character.appearance} />
</View>
<View style={styles.fieldContainer}>
<Text style={styles.fieldLabel}>Avatar</Text>
<View style={styles.pickerContainer}>
<Picker selectedValue={pictureValue} onValueChange={(itemValue, itemIndex) => {pictureValue=itemValue;}} style={styles.picker} >
{names.map((value, index) => {
return <Picker.Item style={styles.pickerItem} label={descriptions[index]} value={value} key={index} />
})}
</Picker>
</View>
</View>
<View style={styles.fieldContainer}>
<Text style={styles.fieldLabel}>Role</Text>
<View style={styles.pickerContainer}>
<Picker selectedValue={role} onValueChange={(itemValue, itemIndex) => {role=itemValue;}} style={styles.picker} >
<Picker.Item style={styles.pickerItem} label="Protagonist" value="Protagonist" />
<Picker.Item style={styles.pickerItem} label="Deuteragonist" value="Deuteragonist" />
<Picker.Item style={styles.pickerItem} label="Antagonist" value="Antagonist" />
<Picker.Item style={styles.pickerItem} label="Major Character" value="Major Character" />
<Picker.Item style={styles.pickerItem} label="Minor Character" value="Minor Character" />
</Picker>
</View>
</View>
<View style={styles.fieldContainer}>
<Text style={styles.fieldLabel}>Motivation/Conflict</Text>
<TextInput value={motiValue} onChangeText={(change) => {motiValue = change;}} style={styles.textInputMultiline} multiline={true} defaultValue={character.motivation} />
</View>
<View style={styles.fieldContainer}>
<Text style={styles.fieldLabel}>Summary of Character</Text>
<TextInput value={summaryValue} onChangeText={(change) => {summaryValue = change;}} style={styles.textInputMultiline} multiline={true} defaultValue={character.summary} />
</View>
<TouchableOpacity style={styles.submit} onPress={charUpdate} >
<Text style={styles.submitText}>Update</Text>
</TouchableOpacity>
</ScrollView>
)
}
const styles = StyleSheet.create({
textInput: {
height: 40,
width: 300,
backgroundColor: '#65AFBF',
borderRadius: 10,
color: 'white',
padding: 10,
fontSize: 18,
fontFamily: 'Baloo-Paaji-2',
},
textInputMultiline: {
width: 300,
height: 160,
backgroundColor: '#65AFBF',
borderRadius: 10,
color: 'white',
padding: 10,
fontSize: 18,
fontFamily: 'Baloo-Paaji-2',
},
picker: {
height: 40,
width: 300,
color: 'white',
},
pickerItem: {
fontFamily: 'Baloo-Paaji-2',
fontSize: 18,
},
pickerContainer: {
backgroundColor: '#65AFBF',
borderRadius: 10,
},
fieldContainer: {
marginTop: 40,
},
header: {
fontSize: 48,
fontFamily: 'Abril-Fatface',
textAlign: 'center',
marginTop: 100,
marginBottom: 10,
color: '#143C44',
},
fieldLabel: {
fontFamily: 'Baloo-Paaji-2',
fontSize: 18,
color: '#143C44',
marginBottom: 5,
},
submit: {
marginVertical: 50,
backgroundColor: '#65AFBF',
alignItems: 'center',
justifyContent: 'center',
height: 50,
width: 100,
borderRadius: 20,
},
submitText: {
fontFamily: 'Baloo-Paaji-2',
fontSize: 18,
color: 'white',
}
})
CharInfo.js
import React from 'react';
import { ScrollView, Text, View, TouchableOpacity, StyleSheet, Image, Dimensions} from 'react-native';
import { useSelector } from 'react-redux';
import Ionicons from 'react-native-vector-icons/Ionicons';
import Constants from 'expo-constants';
import { LinearGradient } from 'expo-linear-gradient';
const width = Dimensions.get('window').width;
const height = Dimensions.get('window').height;
export default function charInfoScreen({ navigation, route }) {
const { id } = route.params;
let character = useSelector(state => state.metaReducer.characters.filter((item) => {if (item.id === id) return item;}))[0];
return (
<ScrollView style={styles.container} >
<View>
{character.picture === 'icon' ? <Ionicons name='ios-person' size={108} color='white' style={{alignSelf: 'center', minHeight: height /2 - 100, paddingTop: Constants.statusBarHeight + 75}} /> : <Image source={character.picture} resizeMode='cover' style={{width: width, height: height / 2 - 100}} />}
</View>
<LinearGradient colors={['#332e30', '#141213']} style={styles.gradient}>
<Text style={styles.fieldName}>Full Name:</Text>
<Text style={styles.majorText}>{character.name}</Text>
<Text style={styles.fieldName}>Age:</Text>
<Text style={styles.majorText}>{character.age}</Text>
<Text style={styles.fieldName}>Profession:</Text>
<Text style={styles.majorText}>{character.profession}</Text>
<Text style={styles.minorText}>{character.name.split(' ')[0]} is a {character.age} year old {character.profession.toLowerCase()}, described as {character.appearance.toLowerCase()}. You have assigned {character.name.split(' ')[0]} a character role of {character.role.toLowerCase()}. {character.name.split(' ')[0]}'s motivation is {character.motivation.toLowerCase()}. In summary, {character.name.split(' ')[0]} {character.summary.toLowerCase()}.</Text>
<TouchableOpacity style={styles.editButton} onPress={navigation.navigate('CharEdit', {id: id})} >
<Ionicons name='chevron-forward-circle' color='white' size={48} />
</TouchableOpacity>
</LinearGradient>
</ScrollView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F0F2F2',
marginHorizontal: 0,
minHeight: height,
},
gradient: {
minHeight: height / 2 + 100,
},
fieldName: {
color: 'white',
marginTop: 25,
marginLeft: 30,
fontFamily: 'Baloo-Paaji-2',
fontSize: 18,
},
majorText: {
color: 'white',
marginLeft: 30,
fontFamily: 'Abril-Fatface',
fontSize: 24,
},
minorText: {
color: 'gray',
marginHorizontal: 30,
marginTop: 25,
fontFamily: 'Baloo-Paaji-2',
fontSize: 16,
},
editButton: {
backgroundColor: 'transparent',
borderRadius: 100,
height: 60,
width: 60,
marginVertical: 30,
marginLeft: width - 90,
alignItems: 'center',
justifyContent: 'center',
}
})
Currently, trying to navigate CharInfo will automatically navigate to CharEdit and will bring up the aforementioned warning. However, if I navigate to CharEdit from any of my other screens, it works perfectly.
From the research I've done, both this question on StackOverflow and this explanation on the React blog suggest that the issue has to do with calling dispatch() in a React Native Hook. However, I'm not using any hooks.
The suggested solution is to wrap where I'm calling dispatch() in the useEffect() Hook. Doing so did not fix the error.
I had the same issue when trying to implement editing and deleting a character on the same screen, which I fixed by moving the deletion logic to a different screen. I can find a workaround, but I'd like to keep this structure if I can.
Here is the error message in full:
Warning: Cannot update a component from inside the function body of a different component.
at node_modules/#react-navigation/native/node_modules/#react-navigation/core/src/useSyncState.tsx:39:22 in React.useCallback$argument_0
at node_modules/#react-navigation/native/node_modules/#react-navigation/core/src/useNavigationBuilder.tsx:309:21 in React.useCallback$argument_0
at node_modules/#react-navigation/native/node_modules/#react-navigation/core/src/SceneView.tsx:67:14 in React.useCallback$argument_0
at node_modules/#react-navigation/native/node_modules/#react-navigation/core/src/useNavigationBuilder.tsx:309:21 in React.useCallback$argument_0
at node_modules/#react-navigation/native/node_modules/#react-navigation/core/src/useOnAction.tsx:105:20 in React.useCallback$argument_0
at node_modules/#react-navigation/native/node_modules/#react-navigation/core/src/useNavigationHelpers.tsx:43:30 in dispatch
at node_modules/#react-navigation/native/node_modules/#react-navigation/core/src/useNavigationCache.tsx:91:10 in dispatch
at node_modules/#react-navigation/native/node_modules/#react-navigation/core/src/useNavigationCache.tsx:122:22 in withStack$argument_0
at node_modules/#react-navigation/native/node_modules/#react-navigation/core/src/useNavigationCache.tsx:109:18 in withStack
at node_modules/#react-navigation/native/node_modules/#react-navigation/core/src/useNavigationCache.tsx:120:21 in acc.name
Can anyone explain why this error is occurring please?
When I submit a form in React Native I get the below error undefined is not an object (evaluating 'title.length'). This problem occurs when I submit a form when the Card.js should be rendering the data from the form. I have checked and its getting the data back fine, seems to be a problem with rendering the data that its reading as undefined. After this error the form actually submits successfully and the Card.js displays the data.
Card.js
import React from "react";
import {
StyleSheet,
View,
Text,
ImageBackground,
TouchableOpacity,
} from "react-native";
const Card = (props) => {
const {
navigation,
title,
address,
homeType,
description,
image,
yearBuilt,
price,
id,
} = props;
return (
<TouchableOpacity
onPress={() => props.navigation.navigate("HomeDetail", { houseId: id })}
>
<View style={styles.card}>
<View style={styles.titleContainer}>
<Text style={styles.title}>
{title.length > 30 ? title.slice(0, 30) + "..." : title}
</Text>
</View>
<View style={styles.imageContainer}>
<ImageBackground source={{ uri: image }} style={styles.image}>
<Text style={styles.price}>${price}</Text>
<View style={styles.year}>
<Text style={styles.yearText}>{yearBuilt}</Text>
</View>
</ImageBackground>
</View>
<View style={styles.description}>
<Text style={styles.descriptionText}>
{description.length > 100
? description.slice(0, 100) + "..."
: description}
</Text>
</View>
</View>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
card: {
shadowColor: "black",
shadowOpacity: 0.25,
shadowOffset: { width: 0, height: 2 },
shadowRadius: 8,
borderRadius: 10,
backgroundColor: "#ffffff",
elevation: 5,
height: 300,
margin: 10,
},
titleContainer: {
height: "15%",
padding: 10,
},
title: {
fontSize: 18,
fontWeight: "bold",
color: "gray",
},
imageContainer: {
width: "100%",
height: "65%",
overflow: "hidden",
},
image: {
width: "100%",
height: "100%",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "flex-end",
},
price: {
fontSize: 30,
color: "#fff",
margin: 10,
},
year: {
margin: 10,
backgroundColor: "#2652B0",
height: 25,
width: 80,
borderRadius: 5,
},
yearText: {
fontSize: 20,
color: "#fff",
textAlign: "center",
},
description: {
margin: 10,
},
descriptionText: {
fontSize: 16,
color: "gray",
},
});
export default Card;
HomeListScreen.js
import React, { useEffect, useState } from "react";
import {
StyleSheet,
View,
Text,
FlatList,
ActivityIndicator,
} from "react-native";
import { FloatingAction } from "react-native-floating-action";
import { useDispatch, useSelector } from "react-redux";
import Card from "../components/Card";
import * as houseAction from "../redux/actions/houseAction";
const HomeListScreen = (props) => {
const dispatch = useDispatch();
const [isLoading, setIsLoading] = useState(false);
const { houses } = useSelector((state) => state.house);
useEffect(() => {
setIsLoading(true);
dispatch(houseAction.fetchHouses())
.then(() => setIsLoading(false))
.catch(() => setIsLoading(false));
}, [dispatch]);
if (isLoading) {
return (
<View style={styles.centered}>
<ActivityIndicator size="large" />
</View>
);
}
if (houses.length === 0 && !isLoading) {
return (
<View style={styles.centered}>
<Text>No home found. You could add one!</Text>
</View>
);
}
return (
<View style={styles.container}>
<FlatList
data={houses}
keyExtractor={(item) => item._id}
renderItem={({ item }) => (
<Card
navigation={props.navigation}
title={item.title}
address={item.address}
homeType={item.homeType}
description={item.description}
price={item.price}
image={item.image}
yearBuilt={item.yearBuilt}
id={item._id}
/>
)}
/>
<FloatingAction
position="right"
animated={false}
showBackground={false}
onPressMain={() => props.navigation.navigate("AddHome")}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
centered: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});
export default HomeListScreen;
<Text style={styles.title}>
{ title ? (title.length > 30 ? title.slice(0, 30) + "..." : title):true}
</Text>
Make sure that title is not undefined.
I am trying to build a react native application with the expo, First I try to build this application with my chrome web browser, it was worked without any issue after that I try to test the application with my android device and I'm getting an exception - "Text strings must be rendered within a <Text> component" HomeScreen.js files. I have no idea why this happened. My code as follows,
/*This is an Example of Grid View in React Native*/
// import * as React from "react";
import React from 'react';
import { Image, FlatList, StyleSheet, View, Text, TouchableOpacity, TextInput } from 'react-native';
import { COLORS } from '../../asserts/Colors/Colors';
import { CATEGORIES } from '../../asserts/mocks/itemListData';
import CategoryGridTitle from '../components/HomeGridTile';
import { HeaderButtons, Item } from 'react-navigation-header-buttons';
import { HeaderButton } from '../components/HeaderButton';
import HomeScreenButton from '../components/HomeScreenButton';
//import all the components we will need
const renderTopBarItems = (topBarItems) => {
return (
<TouchableOpacity
style={styles.topBar}>
<Text style={styles.textStyle}> {topBarItems.item.category} </Text>
</TouchableOpacity>
)
}
const HomeScreen = props => {
const renderGridItem = (itemData) => {
return <CategoryGridTitle
title={itemData.item.title}
image={itemData.item.image}
onSelect={() => {
props.navigation.navigate({
routeName: 'PaymentHandlerScreen',
params: {
categoryId: itemData.item.id
}
});
}} />;
}
// const [images, setImages] = React.useState(picsumImages);
return (
<View style={styles.mainBody}>
<View style={styles.searchContainer}>
<TextInput
placeholder='Search'
style={styles.formField}
placeholderTextColor={'#888888'}
/>
<TouchableOpacity onPress={() => props.navigation.navigate('BarCodeScannerScreen')}
style={styles.saveFormField}>
<Image
source={require('../../../images/barcode.png')}
style={{
width: '100%',
height: '30%',
resizeMode: 'contain',
alignContent: 'center',
}}
/> </TouchableOpacity>
</View>
<View style={styles.tabBar}>
<FlatList
horizontal
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
keyExtractor={(item, index) => item.id}
data={CATEGORIES}
renderItem={renderTopBarItems} />
</View>
<FlatList
keyExtractor={(item, index) => item.id}
data={CATEGORIES}
renderItem={renderGridItem}
numColumns={3} />
<HomeScreenButton style={styles.buttonView} />
</View>
);
};
HomeScreen.navigationOptions = navigationData => {
return {
headerTitle: 'Tickets',
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='profile'
iconName='ios-star'
onPress={() => {
console.log('profile clicked');
}} />
<Item
title='more'
iconName='md-more'
onPress={() => {
console.log('more clicked');
}} />
</HeaderButtons>
)
};
};
export default HomeScreen;
const styles = StyleSheet.create({
mainBody: {
flex: 1,
justifyContent: 'center',
backgroundColor: COLORS.background,
paddingTop: '3%',
},
searchContainer: {
flex: 1,
flexDirection: 'row',
},
tabBar: {
paddingBottom: '3%',
},
topBar: {
width: 150,
borderWidth: 1,
borderRadius: 20,
borderColor: COLORS.primary_blue,
padding: '5%',
marginLeft: '5%',
},
textStyle: {
color: COLORS.primary_blue,
textAlign: 'center',
fontWeight: 'bold',
fontSize: 14,
},
formField: {
flex: 4,
borderWidth: 1,
padding: '4%',
marginLeft: '2%',
borderRadius: 10,
borderColor: COLORS.gray,
backgroundColor: COLORS.gray,
fontSize: 15,
height: '35%',
},
saveFormField: {
flex: 0.5,
justifyContent: 'space-between',
alignItems: 'center',
margin: 10,
},
buttonView: {
position: 'absolute',
bottom: 0,
left: 0,
},
});
Thank you.
I ran into this error a couple of times. RN doesn't like extra spaces in tags. try removing the spaces before and after {topBarItems.item.category}
<Text style={styles.textStyle}>{topBarItems.item.category}</Text>