Async Storage movie watchlist in React Native - javascript

its my first time i create an APP for my study project , i created app of database of movies using TMDB API , and it remain one last step to finish my project , and it's creating WatchList or Plan to Watch , and i have no idea how to make it . please can someone who have idea of how to create it with Async Storage or anything to save watchlist, and where to add it?
I will put my code
this code of Movie screen, and i need a touchableopacity that make save the movie into favorite component
import React, {useEffect, useState} from 'react';
import {
View,
Text,
Image,
Dimensions,
StyleSheet,
Linking,
ImageBackground,
TouchableOpacity,
ScrollView,
FlatList
} from 'react-native';
import {IMAGE_POSTER_URL} from '../config';
import {GET} from '../../Services/API';
import Loader from '../Components/Loader';
import Constants from '../Components/Constants';
import TrendingMovies from '../Components/TrendingMovies';
import TrendingPeople from '../Components/TrendingPeople';
import { createStackNavigator } from "#react-navigation/stack";
import PeopleDetails from '../Components/PeopleDetails.js';
import { LinearGradient } from 'expo-linear-gradient';
import {POSTER_IMAGE} from '../config';
import AsyncStorage from '#react-native-async-storage/async-storage';
const deviceHeight = Dimensions.get('window').height;
const deviceWidth = Dimensions.get('window').width;
const MovieDetails = props => {
const [loading, setLoading] = useState(true);
const [details, setDetails] = useState();
const [favourites, setFavourites] = useState([]);
useEffect(() => {
const getDetails = async () => {
const data = await GET(`/movie/${props.route.params.movieId}`);
setDetails(data);
setLoading(false);
};
getDetails();
}, []);
useEffect(() => {
const getVideo = async () => {
const results = await GET(`/movie/${props.route.params.movieId/videos}`);
setDetails(results);
setLoading(false);
};
getVideo();
}, []);
const getGenre = () => {
return details.genres.map(genre => (
<View >
<Text style={styles.genre}>{genre.name}</Text>
</View>
));
};
return (
<View style={styles.sectionBg}>
{loading ? (
<Loader />
) : (
<View style={{ flex: 1 }} >
<Image
source={{uri: `${IMAGE_POSTER_URL}${details.backdrop_path}`}}
style={styles.imageBg}
/>
<View style={{flexDirection:'row', justifyContent:'space-between', alignItems:'center', marginRight:15, marginTop:10}}>
<TouchableOpacity onPress={() => props.navigation.goBack()}
style={{ marginLeft:20, marginTop: 20, marginBottom:20}}
>
<Image
source={require("../../assets/icons/back.png")}
style={{ width:93/1.4 , height: 50/1.4 }} />
</TouchableOpacity>
<TouchableOpacity
style={{ marginLeft:20, marginTop: 20, marginBottom:20}}
>
<Image
source={require("../../assets/icons/nolicked.png")}
style={{ width:256/5.7 , height: 252/5.7 }} />
</TouchableOpacity>
</View>
<ScrollView style={{ flex:1 ,}} >
<TouchableOpacity onPress={() => {
Linking.openURL('https://www.youtube.com/watch?v=${details.key}');
}}
style={{ marginTop:240, marginLeft: "70%",zIndex:1 }}
>
<Image
source={require("../../assets/icons/youtube.png")}
style={{ width: 75, height: 75}} />
</TouchableOpacity>
<ImageBackground
source={require("../../assets/icons/hmm.png")}
style={{ width:'100%' , height: '84%',zIndex:-1 ,marginTop: -60,marginBottom:20}} >
<View style={{ alignSelf: 'center' , marginTop:40 }} >
<View style={{flexDirection: 'row' }} >
<Image
source={{uri: `${IMAGE_POSTER_URL}${details.poster_path}`}}
style={{ width: 150/1.2 , height: 220/1.2 , borderRadius: 20, marginTop: 40 , marginLeft: 20, zIndex:1 }} />
<View style= {{flexDirection:'column' , }} >
<Text style={styles.detailsMovieTitle}>{details.title}</Text>
<View style={{flexDirection: 'row' , alignItems: 'center', marginLeft: 15 , backgroundColor:'orange' , width:70, marginVertical: 5, borderRadius:10}} >
<Image
source={require("../../assets/icons/star.png")}
style={{ width: 20, height: 20 , marginLeft: 5, marginVertical:8}} />
<Text style= {{color:'#20222A' , fontSize: 18, marginLeft: 6, fontWeight:'bold' ,marginRight: 15}} >{details.vote_average}</Text>
</View>
<View style={{ flexDirection: 'row', width: 80 ,marginTop:25, marginLeft:20}}>
{getGenre()}
</View>
<Text style={{marginLeft: 14,
marginHorizontal:-5,
marginVertical:5,
fontWeight:'bold',
color: '#C3C3C3',
fontSize: 10,}}>{details.runtime} Minutes</Text>
<Text style={{marginLeft: 14,
marginHorizontal:-5,
color: '#C3C3C3',
fontWeight:'bold',
fontSize: 10,}}> Release Date: {details.release_date} </Text>
</View>
</View>
<View style={{marginLeft:15, zIndex:1 , marginBottom:50, marginLeft:20}} >
<Text style={{color:'white', fontSize:16, fontWeight:'bold' , marginBottom: 1,marginLeft: -5}} > Overview </Text>
<Text style={{color: 'white',
fontSize: 12, width:330, marginBottom:15}}>{details.overview}</Text>
<TrendingPeople
title="Actors"
navigation={props.navigation}
url={`/movie/${props.route.params.movieId}/credits`}
isForPage="details"
/>
<View style={{marginLeft: -15, marginTop:10, }} >
<TrendingMovies
title="SIMILAR MOVIES"
navigation={props.navigation}
url={`/movie/${props.route.params.movieId}/similar`}
/>
</View>
</View>
</View>
</ImageBackground>
</ScrollView>
</View>
)}
</View>
);
};
const styles = StyleSheet.create({
sectionBg: {
backgroundColor: Constants.baseColor,
height: deviceHeight,
flex:1
},
trendingPeopleImage: {
height: 70,
width: 70,
borderRadius: 500,
},
trendingPeopleName: {
width: 60,
color: Constants.textColor,
fontSize: 12,
textAlign: 'center',
marginTop: 10,
},
trendingPeopleContainer: {
margin: 10,
},
heading: {
fontSize: 12,
color: 'white',
margin: 10,
fontWeight:'bold'
},
posterImage: {
height: 800,
width: 150,
borderRadius: 10,
},
movieTitle: {
color: Constants.textColor,
width: 150,
textAlign: 'center',
marginTop: 5,
fontSize: 20,
fontWeight:'bold'
},
imageBg: {
position: 'absolute', top:0, left:0 ,
width: deviceWidth,
height: 400,
opacity: 0.8
},
detailsMovieTitle: {
fontSize: 20,
width: 180,
fontWeight:'bold',
color: 'white',
marginLeft:15,
marginTop: 35
},
linkContainer: {
backgroundColor: Constants.secondaryColor,
borderRadius: 100,
padding: 10,
width: 45,
marginLeft: 20,
marginTop: -20,
},
overview: {
color: 'white',
marginHorizontal: 10,
textAlign: 'justify',
fontSize: 16,
},
details: {
color: 'white',
fontSize: 11,
marginLeft: 10,
fontWeight: 'bold',
},
detailsContainer: {
display: 'flex',
marginLeft:15
},
genreContainer: {
borderColor: Constants.textColor,
paddingHorizontal: 0,
},
genre: {
width: 50,
marginHorizontal:-5,
fontWeight:'bold',fontWeight:'bold',
color: '#C3C3C3',
marginRight:4,
fontSize: 9,
},
image : {
height: 100,
width: 180,
borderRadius: 15,
paddingHorizontal: 5,
marginTop: 10,
marginLeft: 20
},
Text: {
marginLeft: 20,
color: 'white',
fontWeight: 'bold',
fontSize: 20
},
title: {
marginTop: 10,
width: 150,
textAlign: 'center',
color: 'white',
fontWeight: 'bold',
fontSize: 12
}
});
export default MovieDetails;```

First you create a favorite state
const [ favorites, setFavorites ] = useState([]);
Then you use useEffect to load the favorite from async storage.
const getData = async () => {
try {
const value = await AsyncStorage.getItem('#storage_key')
if(value !== null) {
setFavorites(JSON.parse('#storage_key'));
}
} catch(e) {
// error reading value
}
}
useEffect(() => {
getData();
}, [])
Then you create a touchable component to add to favorites.
<TouchableOpacity onPress={() => addToFavorites('some_id')}>
Add to Favorites
</TouchableOpacity>
Create the add to favorite function
const addToFavorites = async (id) => {
await AsyncStorage.setItem('#storage_key', JSON.stringify([...favorites, id]))
setFavorites([...favorites, id]);
}
More on Asyncstorage usage here - https://react-native-async-storage.github.io/async-storage/docs/usage

Related

TypeError: undefined is not an object (evaluating '_app.default.initializeApp')

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.

React-native-Agenda how to add a new item to the Agenda

I'm trying to add values ​​through the Modal inputs and I'm able to save and print these values, but I need to add them to the Agenda.
The Agenda asks for an object and even though I created an object, the Agenda says that it is either an array or a string and does not accept the type in the prop items.
What would be the function that will print correctly in the Agenda?
My code:
import React, { useEffect, useState } from 'react'
import { StyleSheet, View, Text, TouchableOpacity, Modal, TextInput, Alert } from 'react-native'
import { LocaleConfig, Agenda } from 'react-native-calendars'
import DateTimePicker from 'react-native-modal-datetime-picker';
import { Feather } from '#expo/vector-icons';
import { Ionicons } from '#expo/vector-icons';
import { Fontisto } from '#expo/vector-icons';
LocaleConfig.locales['tr'] = {
monthNames: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio" ,"Junho" ,"Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
monthNamesShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"],
today: "Hoje",
dayNames: ["Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"],
dayNamesShort: ["Seg", "Ter", "Qua", "Qui", "Sex", "Sáb", "Dom"]
}
LocaleConfig.defaultLocale = "tr"
const Calendario = () => {
const [modalVisible, setModalVisible] = useState(false);
// informações que serão adicionadas ao calendário
const [events, setEvents] = useState(null);
const [dateSelected, setDateSelected] = useState('');
const [description, setDescription] = useState({});
const [refreshCalender, setRefreshCalender] = useState(false);
const item = {
data: "2022-07-26",
title: "teste",
description: "teste"
}
const renderItem = (item) => {
return(
<View style={styles.itemContainer}>
<Text>{item.title}</Text>
<Text>{item.description}</Text>
</View>
)
}
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
const showDatePicker = () => {
setDatePickerVisibility(true);
};
const hideDatePicker = () => {
setDatePickerVisibility(false);
};
const handleConfirm = (date) => {
console.log("A date has been picked: ", date);
setDateSelected(date);
hideDatePicker();
};
return (
<>
<Agenda
items={item}
renderEmptyDate={() => {
return <View />;
}}
renderEmptyData={() => {
return <View />;
}}
selected={new Date()}
minDate={null}
renderItem={renderItem}
markingType="custom"
refreshing={refreshCalender}
/>
<View>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert('Modal has been closed.');
setModalVisible(!modalVisible);
}}>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Text style={styles.modalText}>Adicione um horário</Text>
<TextInput
placeholder='Título'
onChangeText={(text) => setEvents(text)}
style={styles.input}
/>
<TextInput
placeholder='Descrição'
onChangeText={(text) => setDescription(text)}
style={styles.input}
/>
<TouchableOpacity
style={styles.btnDatePicker}
onPress={showDatePicker}
>
<Text>Selecionar data</Text>
<Fontisto name="date" size={20} color="#fff" style={{ margin: 10 }} />
</TouchableOpacity>
<DateTimePicker
mode='date'
minimumDate={null}
isVisible={isDatePickerVisible}
onConfirm={handleConfirm}
onCancel={hideDatePicker}
/>
<TouchableOpacity
style={[styles.button2, styles.buttonClose2]}
onPress={() => {
console.log(events)
console.log(description)
console.log(dateSelected)
console.log(typeof(dateSelected))
setModalVisible(false)
}}>
<Text style={styles.textStyle}>
<Ionicons name="checkmark-sharp" size={24} color="black" />
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.buttonClose]}
onPress={() => setModalVisible(!modalVisible)}>
<Text style={styles.textStyle}>
<Feather name="x-circle" size={24} color="black" />
</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
<TouchableOpacity style={styles.btn}
onPress={() => setModalVisible(true)}
>
<Ionicons name={'add-outline'} size={30} color={'black'}/>
</TouchableOpacity>
</View>
</>
)
}
export default Calendario
const styles = StyleSheet.create({
itemContainer: {
backgroundColor: "#fff",
margin: 5,
borderRadius: 15,
justifyContent: "center",
alignItems: "center",
flex: 1,
},
centeredView: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
marginTop: 22,
},
modalView: {
margin: 20,
backgroundColor: 'white',
borderRadius: 20,
width: "90%",
height: "60%",
alignItems: 'center',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
button: {
borderRadius: 20,
padding: 10,
elevation: 2,
position: "absolute",
right: 0,
bottom: 0,
margin: 15,
},
button2: {
borderRadius: 20,
padding: 10,
elevation: 2,
position: "absolute",
left: 0,
bottom: 0,
margin: 15,
},
buttonOpen: {
backgroundColor: '#F194FF',
},
buttonClose: {
backgroundColor: 'red',
},
buttonClose2: {
backgroundColor: '#9beb34',
},
textStyle: {
color: 'white',
fontWeight: 'bold',
textAlign: 'center',
},
modalText: {
marginBottom: 15,
textAlign: 'center',
fontWeight: "bold",
fontSize: 24,
margin: 10
},
btn: {
position: "absolute",
right: 0,
bottom: 0,
backgroundColor: "#0782F9",
padding: 10,
borderRadius: 30,
alignItems: "center",
margin: 15
},
input: {
width: "90%",
backgroundColor: "white",
paddingHorizontal: 15,
paddingVertical: 10,
borderRadius: 10,
marginTop: 5,
borderBottomWidth: 2,
borderBottomColor: "#313131"
},
dateComponent: {
width: "60%",
marginTop: 20,
marginBottom: 5,
},
btnDatePicker: {
padding: 15,
marginHorizontal: 10,
alignItems: "center",
backgroundColor: "#0782F9",
marginTop: 20,
width: "40%",
borderRadius: 15,
},
});
As per library mention you item is object and this object contains a key as a date and this key contain array so you item data look like
const item = {
"2022-07-26":[{name:"teste",description: "teste"}]
}
return (
<>
<Agenda
items={item}
renderEmptyDate={() => {
return <View />;
}}
renderEmptyData={() => {
return <View />;
}}
selected={new Date()}
minDate={null}
renderItem={renderItem}
markingType="custom"
refreshing={refreshCalender}
/>
</>
)
}
export default Calendario

Navigating to a specific screen introduces a breaking bug

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?

horizontal ScrollView is not scrolling

When adding horizontal={true} to my scrollview, I thought that would be enough to be able to scroll sideways. For some reason, even though there is enough content to scroll to, the images will not scroll continuously. If you copy and paste this code into snack.expo.io you will see what I mean.
I am not sure what is causing this issue, as I know the normal scrollview vertically works fine and scrolls like normal. I have also tried using nestedScrollenabled to true
Any insight at all is appreciated more than you know!
import React, { useState } from 'react';
import {Pressable, StyleSheet, Text, View, useWindowDimensions, Dimensions, Image, Animated, PanResponder,
TouchableOpacity, ScrollView, ImageBackground, Platform} from 'react-native';
import { SearchBar } from 'react-native-elements';
import {
scale,
verticalScale,
moderateScale,
ScaledSheet,
} from 'react-native-size-matters';
import { MaterialCommunityIcons } from '#expo/vector-icons';
const Images = [
{ id: '1', uri: require('./assets/snack-icon.png'), text: 'Test' },
{ id: '2', uri: require('./assets/snack-icon.png') /*text: "Test"*/ },
{ id: '3', uri: require('./assets/snack-icon.png') /*text: "Test"*/ },
{ id: '4', uri: require('./assets/snack-icon.png') /*text: "Test"*/ },
]
const pressableTest = () => {
let textlog = '';
const [state, setState] = useState(0);
};
export default class Home extends React.Component {
renderImagesHorizontal = () => {
return Images.map((item, i) => {
return (
<View
style={{
width : '150%',
paddingLeft: scale(10),
paddingRight: scale(10),
paddingBottom: scale(15),
}}>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('VenueDetails')}>
<ImageBackground
source={item.uri}
style={{
width: '100%',
height: scale(225),
shadowColor: '#000',
shadowOffset: { width: 1, height: 4 },
shadowOpacity: 1,
}}
imageStyle={{ borderRadius: 10 }}>
<View
style={{
position: 'absolute',
bottom: 10,
left: 10,
justifyContent: 'flex-start',
alignItems: 'flex-start',
}}>
<Text style={styles.name}>Name</Text>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Text style={styles.category}>Category</Text>
<Text style={styles.dot}>⬤</Text>
<Text style={styles.money}>$$</Text>
<Text style={styles.dot}>⬤</Text>
<Text style={styles.starRating}>★★★</Text>
</View>
</View>
</ImageBackground>
</TouchableOpacity>
</View>
);
});
};
renderImagesVertical = () => {
return Images.map((item, i) => {
return (
<View style={{ paddingLeft: scale(10), paddingRight: scale(10), paddingBottom: scale(20) }}>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('VenueDetails')}>
<ImageBackground
source={item.uri}
style={{ width:'100%', height: scale(125),
shadowColor: '#000',
shadowOffset: {width: 1, height: 7},
shadowOpacity: 1,}} imageStyle = {{ borderRadius: 20}}>
<View
style={{
position: 'absolute',
bottom: 10,
left: 10,
justifyContent: 'flex-start',
alignItems: 'flex-start',
}}>
<Text style={styles.name}>Name</Text>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Text style={styles.category}>Category</Text>
<Text style={styles.dot}>⬤</Text>
<Text style={styles.money}>$$</Text>
<Text style={styles.dot}>⬤</Text>
<Text style={styles.starRating}>★★★</Text>
</View>
</View>
</ImageBackground>
</TouchableOpacity>
</View>
);
});
};
state = {
search: '',
};
updateSearch = (search) => {
this.setState({ search });
};
render() {
const { search } = this.state;
return (
<ScrollView style={{ flex: 1, backgroundColor: '#272933', horizontal: 'true' }}>
<View style={{flexDirection:'row', marginTop: scale(20)}}>
{/*this will proabbly say somethign different and probably have a different look to it but you get the idea
I was also trying to add a shadow to this but couldnt figure it out. */}
<Text style={{marginTop: scale(30) ,fontSize: scale(40), fontWeight: 'bold', color: '#FFFFFF', paddingLeft: scale(20)}}>
Home
</Text>
<View style={{paddingTop: scale(40), paddingLeft: scale(155)}}>
</View>
</View>
<SearchBar
placeholder="Search..."
onChangeText={this.updateSearch}
value={search}
round='true'
containerStyle={{backgroundColor: '#272933', borderBottomColor: 'transparent', borderTopColor: 'transparent',
paddingLeft: scale(20) , paddingRight: scale(20)}}
inputContainerStyle={{height: scale(30), width: scale(310), backgroundColor: '#3A3B3C'}}
searchIcon={() => <MaterialCommunityIcons name="glass-mug-variant" size={25} color='#87909A'/>}
clearIcon= 'null'
/>
<ScrollView
horizontal={true}
>
<View style={{ flex: 1, flexDirection : 'row', marginTop: 15 }}>{this.renderImagesHorizontal()}</View>
</ScrollView>
<View style={{ flex: 1, marginTop: 15 }}>{this.renderImagesVertical()}</View>
</ScrollView>
);
}
}
const styles = ScaledSheet.create({
starRating: {
color: 'white',
fontSize: '20#s',
textShadowOffset: { width: 2, height: 2 },
textShadowRadius: 2,
textShadowColor: '#000',
},
category: {
color: 'white',
fontSize: '20#s',
textShadowOffset: { width: 2, height: 2 },
textShadowRadius: 2,
textShadowColor: '#000',
},
name: {
fontWeight: 'bold',
color: 'white',
fontSize: '25#s',
textShadowOffset: { width: 2, height: 2 },
textShadowRadius: 2,
textShadowColor: '#000',
},
dot: {
color: 'white',
fontSize: '5#s',
paddingLeft: '5#s',
paddingRight: '5#s',
textShadowOffset: { width: 2, height: 2 },
textShadowRadius: 2,
textShadowColor: '#000',
},
money: {
color: 'white',
fontSize: '20#s',
},
});
in android you must add nestedScrollEnabled={true} to Enables nested scrolling for Android API level 21+. see here
<ScrollView>
<ScrollView nestedScrollEnabled={true}>
</ScrollView>
</ScrollView>
try snack here (test in android & ios not web)

Getting list from firebase and rendering it doesn't work

Problem
I am creating an app with react native and firebase where users can post to firebase, and then my app will render all of the posts. When I added the code to render the posts from firebase (flatlist), nothing renders. I convert the list from firebase to an array, then use that array in a flatlist to render the posts, but nothing is showing up. I would love some help.
Code
import React, { Component } from 'react';
import { StyleSheet, Text, View, Image, TextInput, ScrollView, TouchableHighlight, Button, FlatList } from 'react-native';
import { Font } from 'expo';
import * as firebase from 'firebase';
const firebaseConfig = {
apiKey: "API_KEY",
authDomain: "candidtwo.firebaseapp.com",
databaseURL: "https://candidtwo.firebaseio.com",
storageBucket: "candidtwo.appspot.com",
};
const firebaseApp = firebase.initializeApp(firebaseConfig);
var fontLoaded = false;
var ref = firebase.database().ref('posts');
var newPostRef = ref.push();
var postWidth = 350;
export default class App extends React.Component {
state = {
fontLoaded: false,
};
componentDidMount() {
Expo.Font.loadAsync({
'JosefinSans-Regular.ttf': require('./JosefinSans-Regular.ttf'),
});
}
constructor(props) {
super(props);
this.state = { postInput: ""}
}
getItems() {
var items = [];
var query = ref.orderByKey();
query.once ('value', (snap) => {
snap.forEach ( (child) => {
items.push(childs.val());
});
});
return items;
}
renderItem({ items, index }) {
return <View>
<View style={{width: parseInt(this.state.postWidth), height: 250, backgroundColor: '#1daff1', alignItems: 'center', justifyContent: 'center', borderRadius: 10, paddingLeft: 10, paddingRight:10}}>
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', textAlign: 'center'}}>
{toString(reverse(items))}
</Text>
</View>
<View style={{width: parseInt(this.state.postWidth), height: 40, borderRadius: 10, backgroundColor: '#147aa8', flexDirection: 'row',paddingLeft:5}} >
<Image source={require('./CandidtwoImages/unlove.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
-
</Text>
<Image source={require('./CandidtwoImages/undislike.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
-
</Text>
<Image source={require('./CandidtwoImages/comments.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
-
</Text>
</View>
<View style={{width: 1, height: 6, backgroundColor: '#e8e8e8'}} />
</View>;
}
render() {
return (
<View style={styles.container}>
<View style={styles.button}>
<View style={{width: 1, height: 30, backgroundColor: '#e8e8e8'}} />
<Button
onPress={() => this.setState({ fontLoaded: true })}
title="Press Me To Load the App After 15 Seconds!"
color="#fe8200"
accessibilityLabel="Get started anonymously!"
/>
</View>
{this.state.fontLoaded ? (
<View style={styles.container}>
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 16 }}>
Whats on your mind? Create a post!
</Text>
<TextInput
style={{height:40, width: 320, borderColor: '#303030', borderWidth: 1}}
onChangeText={(postInput)=>this.setState({postInput})}
value={this.state.postInput}
/>
<Button
style={{justifyContent: 'center'}}
onPress={() => {
newPostRef.set({ content:this.state.postInput });
this.setState({ postInput: "Your post was succsesfully uploaded! :)" })
}}
title=" + "
color="#fe8200"
/>
<ScrollView>
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 16 }}>
Adjust the size of posts:
</Text>
<TextInput
style={{height:40, width: 100, borderColor: '#303030', borderWidth: 1}}
onChangeText={(postWidth)=>this.setState({postWidth})}
value={this.state.postWidth}
/>
<View style={{width: 1, height: 6, backgroundColor: '#e8e8e8'}} />
<View style={{width: parseInt(this.state.postWidth), height: 250, backgroundColor: '#1daff1', alignItems: 'center', justifyContent: 'center', borderRadius: 10, paddingLeft: 10, paddingRight:10}} >
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', textAlign: 'center'}}>
Why do android phones have higher inital quality than apple phones, but apple phones have a more consistent amount of quality throughout their years?
</Text>
</View>
<View style={{width: parseInt(this.state.postWidth), height: 40, borderRadius: 10, backgroundColor: '#147aa8', flexDirection: 'row',paddingLeft:5}} >
<Image source={require('./CandidtwoImages/unlove.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
3
</Text>
<Image source={require('./CandidtwoImages/undislike.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
1
</Text>
<Image source={require('./CandidtwoImages/comments.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
8
</Text>
</View>
<FlatList
data={this.getItems}
renderItem={this.renderItem}
/>
</ScrollView>
</View>) : (null) }
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 8,
backgroundColor: '#e8e8e8',
alignItems: 'center'
},
button: {
flex: 1,
backgroundColor: '#e8e8e8',
alignItems: 'center'
},
});
Picture of firebase database
id have getItems() in your componentWillMount method, and have it set a state ( Such as "firebaseItems" ) or such. Then in your FlatList have your data equal {this.state.firebaseItems}. This will force a re-render once you receive your information. EG
componentWillMount(){
this.getItems();
}
getItems(){
var items = [];
var query = ref.orderByKey();
query.once ('value', (snap) => {
snap.forEach ( (child) => {
items.push(childs.val());
});
}).then(() => {
this.setState({firebaseItems: items});
});
}
then later
<FlatList data = {this.state.firebaseItems}.../>

Categories