I'm wondering how to show a genre to each movie in the list. So I have already other details like title, poster_path or description.
The problem comes when I'm trying to show a genre's becouse they are a numbers and I don't know how to translate them to string like 'Horror'
Here is code for fetch data:
fetch(
`https://api.themoviedb.org/3/search/movie?&api_key=${
this.apiKey
}&query=${searchTerm}`,
)
.then(data => data.json())
.then(data => {
const results = data.results;
const movieRows = [];
const movieGen = [];
results.forEach(movie => {
movie.poster_path =
'https://image.tmdb.org/t/p/w500' + movie.poster_path;
const movies = <MovieRow key={movie.id} movie={movie} />;
movieRows.push(movies);
});
this.setState({rows: movieRows});
});
}
and also diplay it in custom component like movie card:
viewMore = () => {
Alert.alert(
`PRODUCTION : ${this.props.movie.original_language}`,
`DESCRIPTION : ${this.props.movie.overview}\n \n GENRE : ${
this.props.movie.genre_ids
}`,
);
};
render() {
return (
<View
style={{
width: '100%',
alignItems: 'center',
justifyContent: 'center',
}}>
<CardCustom
title={this.props.movie.title}
popularity={this.props.movie.popularity}
vote_count={this.props.movie.vote_count}
poster_path={this.props.movie.poster_path}
onPress={this.viewMore}
/>
</View>
);
}
}
export default MovieRow;
This how this looks like in the application:
and the response from api for genre_ids looks like that
I noticed that I have to use separate API for genre's. Now I want to match them to current movie and I dont know how to do it.
Here is a code
class MovieRow extends Component {
constructor() {
super();
this.apiKey = '1bd87bc8f44f05134b3cff209a473d2e';
this.state = {};
}
viewMore = () => {
Alert.alert(
`PRODUCTION : ${this.props.movie.original_language}`,
`DESCRIPTION : ${this.props.movie.overview}\n \n
GENRE : ${this.props.movie.genre_ids}`, // < ------ NUMBER DISPLAYS. HOW TO MATCH GENRE WITH CURRENT MOVIE?
);
this.fetchGenre();
};
fetchGenre() {
fetch(
`https://api.themoviedb.org/3/genre/movie/list?&api_key=${this.apiKey}`,
)
.then(data => data.json())
.then(data => {
const resultGenres = data.genres;
const genreRow = [];
console.log(resultGenres);
resultGenres.map(genre => {
console.log('name', genre.name, 'id', genre.id);
const genres = <Text>genre: {genre.name}</Text>;
genreRow.push(genres);
});
this.setState({gen: genreRow});
});
}
render() {
return (
<View
style={{
width: '100%',
alignItems: 'center',
justifyContent: 'center',
}}>
<CardCustom
title={this.props.movie.title}
popularity={this.props.movie.popularity}
vote_count={this.props.movie.vote_count}
poster_path={this.props.movie.poster_path}
onPress={this.viewMore}
/>
{this.state.gen}
</View>
);
}
}
also this is how response looks like
Regards
Just get an array with all ids of genders and store it into your state, then when you want to display it you will just use a map. like so :
this.state.gender_ids = [
1: "Action",
2: "Horror",
3: "Other gender"
]
this.props.movie.genre_ids.map(id => <Text key={this.state.gender_ids[id]}>{this.state.gender_ids[id]}</Text>)
Just run the following code into your browser's console, I'm pretty sure from now on you'll get the job done.
Example for pairing :
let gendersFromServer = [
{
id: 28,
name: "Action"
},
{
id: 12,
name: "Adventure"
},
{
id: 16,
name: "Animation"
},
// other genders here
]
let gender_ids = [] // intialize with an empty array
gendersFromServer.map(el => gender_ids[el.id] = el.name) // here you transform the data
// here you can setState({gender_ids})
const movie = {
gender_ids: [
28,
12,
16
]
// rest of data
}
// how to get text gender, notice that gender_ids from console log is the one you use in state, not the one from the movie
movie.gender_ids.map(id => console.log(gender_ids[id]))
EDIT 2:
Hope this will solve your problem finally
import React from 'react'
import { SafeAreaView, ScrollView, View, Text } from 'react-native'
const API_KEY = '1bd87bc8f44f05134b3cff209a473d2e'
export default props => {
const [genres, setGenres] = React.useState([])
const [movies, setMovies] = React.useState([])
React.useEffect(() => {
fetch('https://api.themoviedb.org/3/search/movie?&query=Now+You+See+Me&api_key=' + API_KEY)
.then(res => res.json())
.then(result => {
setMovies(result.results)
})
fetch('https://api.themoviedb.org/3/genre/movie/list?&api_key=' + API_KEY)
.then(genre => genre.json())
.then(result => {
const genres = result.genres.reduce((genres,gen) => {
const { id, name } = gen
genres[id] = name
return genres
},[])
setGenres(genres)
})
},[])
const Movies = () => movies.map(movie => {
return (
<View>
<Text>{movie.title}</Text>
<View>
<Text>Genres :</Text>
{
movie.genre_ids.map(id => {
return <Text>{genres[id]}</Text>
})
}
</View>
</View>
)
})
return (
<SafeAreaView style={{flex: 1}}>
<ScrollView style={{flex: 1}}>
<Text>Movies here</Text>
<Movies />
</ScrollView>
</SafeAreaView>
)
}
Related
inside userlist.js the code looks like
const renderItems = ({item}) => {
if (item !== undefined) {
const user = {
image:'https://www.w3schools.com/w3images/avatar6.png',
name: item.name,
lastMessage: item.lastChat,
time: item.latest_timestamp,
seen: item.seen,
id: item._id,
};
if(item._id!==userID)return <DocList user={user} nav={nav} sendToParents={fn} from="userlist"/>;
else return null;
}
return null;
};
const DATA = [
{
data: friends,
},
];
return (
<View style={{marginTop:53,marginBottom:130}}>
{friends.length==0?
<View style={{alignSelf:'center',marginTop:90}}>
<Image style={{resizeMode:'stretch',width:350,height:220,alignSelf:'center'}} source={require('../../asserts/No_chats.png')} />
<Text style={{textAlign:'center',fontWeight:'bold',marginTop:10,color:'#838383'}}>No recent chats !</Text>
<Text style={{textAlign:'center',textAlign:'center',width:300,alignSelf:'center',color:'gray'}}>Not yet started conversation? press the green + icon to see the list of users you can chat with.</Text>
</View>
:
<SectionList
sections={DATA}
keyExtractor={(item, index) => index.toString()}
renderItem={renderItems}
renderSectionHeader={({section: {title}}) => (
<View>{title}</View>
)}>
</SectionList> }
</View>
);
};
inside newuser.js it looks like
const handleRender = ({ item }) => {
const user = {
image: 'https://www.w3schools.com/w3images/avatar6.png',
name: item.name,
lastMessage: item.lastChat,
time: item.latest_timestamp,
seen: item.seen,
id: item._id,
};
return (
<DocList user={user} nav={nav} sendToParents={handleCallback} from="newuser" />
);
};
return (
<View>
<FlatList
data={docs}
keyExtractor={(item, index) => index.toString()}
renderItem={handleRender}
/>
</View>
);
};
The DocList component is:
import {socketSubsribe, socketIdentity, socketUnsubscribe} from './SocketServer';
import axios from 'axios';
import {StyleSheet, Text, View, Image, TouchableOpacity} from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
import {BASE_URL, BASE_URL1} from '../../Components/Urls';
import { nowTime } from '../../Components/Clock';
import socket from "../components/SocketServer";
const DocList = ({user, nav, sendToParents,from}) => {
const [latestmsg,setLatestmsg]=useState(user.lastMessage);
const [userImg,setUserImg]=useState(null);
const [seen,setSeen]=useState(user.seen);
const [roomID,setRoomID]=useState();
let latmsg;
// const textBold = user.seen ? 'normal' : 'bold';
const textBold ='bold';
const subscribe=async()=>{
//await AsyncStorage.removeItem("chatUsers");
const resultRoomidChat= await AsyncStorage.getItem(`${user.id}`);
if(resultRoomidChat){
const {roomId,chat}=JSON.parse(resultRoomidChat);
console.log(roomId,"ROOMId");
setRoomID(roomId);
}
const { userId,userToken} = JSON.parse(
await AsyncStorage.getItem('userToken')
);
const GET_IMAGE_URL=BASE_URL1+"/api/user/getUserImg";
const resultUser= await axios.get(GET_IMAGE_URL+`/${user.id}`,{
headers:{
authorization:`Bearer ${userToken}`
}
});
//console.log("RESULTUSER",resultUser.data.picture,typeof(resultUser.data.picture));
if(resultUser.data.picture){
// console.log("USER Image",resultUser.data.picture);
let imgsrc=resultUser.data.picture;
setUserImg(imgsrc);
//console.log("IMGSRC",imgsrc);
}
socketIdentity(userId);
//console.log("SUNSCRIBE IDENTITY");
const object =await AsyncStorage.getItem(`${user.id}`);
if(object){
//console.log("SUNSCRIBE OTHERUSER");
const {roomId}=JSON.parse(object);
socketSubsribe(roomId, user.id);
}
}
const handleRequest = async () => {
try {
const {userToken, userId} = JSON.parse(
await AsyncStorage.getItem('userToken'),
);
const URL= BASE_URL+'/api/chat/initiate';
const data={
userIds: [userId, user.id],
type: 'customer-to-doctor',
}
console.log("1",URL, data);
const res= await axios.post(URL,data,{
headers:{
authorization:`Bearer ${userToken}`
}
})
console.log("2",res.data,res.status);
if (res.data && res.status ) {
const {chatRoom} = res.data;
const id = chatRoom.chatRoomId;
console.log("CHATROOM ID iS",id)
const object1=await AsyncStorage.getItem(`${user.id}`);
console.log("object1 ",object1);
if(object1){
const object=JSON.parse(object1);
const modifiedObject={
roomId:id,
medium:'app',
chat:[...object.chat]
}
await AsyncStorage.setItem(`${user.id}`, JSON.stringify(modifiedObject));
console.log("USER.ID",user.id,modifiedObject);
}else{
await AsyncStorage.setItem(`${user.id}`, JSON.stringify({roomId: id,medium:'app', chat:[]}));console.log("after setting ROOM ID");
}
}
} catch (e) {
console.log(e, '==>> error in app doclist');
}
};
const call2doclist=async(msg,postedByUser,seen)=>{
try{
const {userInfo} = JSON.parse(
await AsyncStorage.getItem('chatUsers'),
);
const {userId} = JSON.parse(
await AsyncStorage.getItem('userToken'),
);
//console.log("uI in DOCLIST");
if(userInfo){
console.log("hi call2doclist");
let i=0;
for(i=0;i<userInfo.length;i++){
if(userInfo[i]._id==user.id){
//console.log("hi3");
console.log("POSTEDBYVUSER CHECK",postedByUser,userId,user.id);
if(seen==true && postedByUser==userId){
console.log('hi4');
userInfo[i].seen=true;
setSeen(true);
}else{
userInfo[i].seen=false;
setSeen(false);
}
userInfo[i].lastChat=msg;
setLatestmsg(msg);
console.log("userInfo[i]",userInfo[i],seen);
break;
}
}
await AsyncStorage.setItem(
'chatUsers',
JSON.stringify({userInfo:[...userInfo]}),
);
}
user.lastMessage=msg;
setLatestmsg(user.lastMessage);
console.log("SEEN",seen);
}catch(e){
console.log("Error in call2 inside doclist",e)
}
}
const handlePress = async () => {
setLatestmsg('');
console.log(latestmsg,from);
await handleRequest();
if(from=="newuser"){
sendToParents(user.id);
}
console.log("inside doclist",user);
console.log("hey");
nav.navigate('ChatSc', {name: user.name, id: user.id ,from:'app',call2doclist:{call2doclist}});
};
const updateChat=async(msg)=>{
try{
const data = JSON.parse(await AsyncStorage.getItem(`${user.id}`));
await AsyncStorage.removeItem(
`${user.id}`
);
data.chat.unshift({
side:msg.side,
text:msg.text,
message_id:msg.message_id,
time: msg.time,
});
//console.log("i saev");
await AsyncStorage.setItem(
`${user.id}`,
JSON.stringify({...data}),
);
}catch(e){
console.log("Error in updating chats",e);
}
}
const update2=async(msg)=>{
await updateChat(msg)
}
socket.off('new message'+`${roomID}`).on('new message'+`${roomID}`,(data,err)=>{
try{
if(err){ console.log("error in socket recieving",err)}
if(!err){
console.log("inside DOCLIST for app");
const msg={
// postedByUser:data.message.postedByUser._id,
message_id:data.message.postId,
side:user.id === data.message.postedByUser._id ? 'left':'right',
text:data.message.message.messageText,
time:data.message.message.time,
timestamp:nowTime()
};
update2(msg);
call2doclist(data.message.message.messageText,data.message.postedByUser._id,false);
}
}catch(e){
console.log(e,"Error in chatScreen of inside doclist ",e.message)
}
});
const subscribe2=async()=>{
await subscribe();
console.log("hi");
}
useEffect(()=>{
subscribe2();
},[]);
return (
<TouchableOpacity onPress={()=>handlePress()}>
<View style={styles.topContainer}>
{/* user Image */}
<View style={{...styles.ImageContainer}}>
<Image source={{uri:userImg?userImg:user.image}} style={{...styles.imageStyle}} />
</View>
{/* user data value */}
<View style={styles.userContainer}>
<View style={styles.userInfo}>
<View>
<Text>{user.name}</Text>
<Text style={{fontWeight: textBold,paddingRight:30}}>{seen==false?latestmsg:''}</Text>
</View>
<View>
</View>
</View>
</View>
</View>
</TouchableOpacity>
);
};
export default DocList;
const styles = StyleSheet.create({
imageStyle: {
width: 70,
height: 70,
borderRadius: 70,
},
topContainer: {
paddingLeft:10,
flexDirection: 'row',
width: '100%',
height: 78,
marginTop: 10,
marginBottom: 10,
paddingTop: 0,
},
ImageContainer: {
width: 70,
alignItems: 'flex-end',
},
userContainer: {
flex: 1,
marginLeft: 5,
justifyContent: 'space-around',
},
userInfo: {
flexDirection: 'row',
justifyContent: 'space-between',
padding: 5,
},
});
through userlist.js the console.log prints 2, response data and status , but through newuser it doesn't print these things although it prints 1, URL, data. It means there's some problem there itself but in the former case, it does hit the API and logs in the backend too, but in the latter case it doesn't hit and doesn't log anything there in the backend.
Check if the item parameter is correctly passed to the handleRender function in newuser.js. Also check in the Networks Tab in console whether is it actually hitting the API or because of authentication issue the call must be getting rejected.
I have a stacknavigator and in headerTitle have a header component for each screen, heres the code:
const Home_stack = createStackNavigator({ //hooks
Home: {
screen: Home,
navigationOptions: ({navigation}) => {
return {
headerTitle: () => <Header navigation = {navigation} title = "Shum Note"/>}
}
},
Create: {
screen: Create,
navigationOptions: ({navigation}) => {
return {
headerTitle: () => <Childs_header navigation = {navigation} title = "Create Note"/>}
}
},
Edit: {
screen: Edit,
navigationOptions: ({navigation}) => {
return {
headerTitle: () => <Childs_header navigation = {navigation} title = "Edit Note"/>}
}
},
});
and this is the component Childs_header:
import Create_note from "../components/Create_note";
class Header extends Component {
comun = new Create_note();
render() {
return (
<>
<View style ={{backgroundColor: "white", flexDirection: "row", alignItems: "center"}}>
<View>
<Text style = {{color: "black", fontSize: 30, marginLeft: -20}}>{this.props.title}
</Text>
</View>
<View>
<Text>
<Feather name="check-square" size={24} color="black" onPress = {() => this.comun.save_data(this.props.navigation)}/>
</Text>
</View>
</View>
</>
);
}
}
export default Header;
as you can see I import the component Create_note and create an object of it to use one of its function, in this case save_data, but for some reason it isnt working, dont know if it has something to do with AsyncStorage becase with console.log("hi") it works, but saving data it doesnt, heres the structure of create_note component:
class Create_note extends Component {
state = {
content: "",
default_color: "#87cefa", //default color (cyan)
}
save_data = async() => {
if (this.state.content === "") {
//navigation.navigate("Home");
}else {
let clear_content = this.state.content.replace(/ /g,""); //replace al
try {
const data = await AsyncStorage.getItem("data");
if (data === null) {
const data = {"array_notes": [], "last_note": 0};
const last_note = data.last_note + 1;
const new_note = {note_number: last_note, content: clear_content, color: this.state.default_color, text_color: this.state.color}; //create a new_note object, note_number will be the key for each note
const array_notes = [];
array_notes.push(new_note);
data.array_notes = array_notes;
data.last_note = last_note;
await AsyncStorage.setItem("data", JSON.stringify(data)); //using stringify to save the array
//navigation.navigate("Home");
}else {
const data = JSON.parse(await AsyncStorage.getItem("data")); //use parse to acces to the data of the array
const last_note = data.last_note + 1;
const new_note = {note_number: last_note, content: clear_content, color: this.state.default_color, text_color: this.state.color};
const array_notes = data.array_notes;
array_notes.push(new_note);
data.array_notes = array_notes;
data.last_note = last_note;
await AsyncStorage.setItem("data", JSON.stringify(data));
//navigation.navigate("Home");
}
}catch(error) {
alert(error);
}
}
}
render() {
const props = {
screen: "create_note",
change_color: this.change_color.bind(this),
update_color: this.update_color.bind(this),
}
return (
<>
<ScrollView>
<RichEditor
ref = {this.richText}
onChange = {text => this.setState({content: text}, () => console.log(this.state.content))}
allowFileAccess = {true}>
</RichEditor>
</ScrollView>
{this.state.change_color ?
<Color
{...props}>
</Color>
: null}
<RichToolbar
editor = {this.richText}
onPressAddImage = {this.insertImage}
actions = {[
actions.insertBulletsList,
actions.insertOrderedList,
actions.insertImage,
"change_text_color",
]}
iconMap ={{
[actions.insertBulletsList]: () => <Text style = {this.styles.icon}><MaterialIcons name = "format-list-bulleted" size = {this.option_icon.size} color = {this.option_icon.color}/></Text>,
[actions.insertOrderedList]: () => <Text style = {this.styles.icon}><MaterialIcons name = "format-list-numbered" size = {this.option_icon.size} color = {this.option_icon.color}/></Text>,
[actions.insertImage]: () => <Text style = {this.styles.icon}><MaterialIcons name = "image" size = {this.option_icon.size} color = {this.option_icon.color}/></Text>,
change_text_color: () => <Text style = {this.styles.icon}><MaterialIcons name = "format-color-text" size = {this.option_icon.size} color = {this.option_icon.color}/></Text>,
}}
change_text_color = {this.change_color}
style = {{backgroundColor: "white"}}>
</RichToolbar>
<Button title = "save" onPress = {this.save_data}></Button>
</>
);
}
heres an image so you can see better the structure:
the function should run when I click in the check icon, in the blue button works because its part of the create_note component, but I want it in the check icon
From looking at your code I think the problem is that you're passing the navigation state object as a parameter to your save_data function in the onClick of your checkmark.
this.comun.save_data(this.props.navigation)
but the function definition of save_data doesn't take any parameters:
save_data = async () => {
// ...
};
So you could change the save_data function to something like this
save_data = async (navigation) => {
// ...
};
in order to have it work from inside the Header component.
If you want the save button, rendered by the Create_note component, to also call save_data onPress; you will have to pass the navigation state there as well.
So I need to get data with a given id from a firebase collection. My function then should return it (the document) and in my test it should print (as the 'result'). For some reason, my test prints 'undefined' but my function (getIteration(id)) prints out exactly what I need it to return (at the console.log above the return). Why is it returning undefined but with the same doc.data() printing exactly what I need?
Here is my code:
//gets the iteration with the given id
async function getIteration(id) {
fb.db.collection('iteration').where('id', '==', id).get().then(snapshot => {
snapshot.forEach(doc => {
console.log(doc.data())
return doc.data();
})
})
}
and my test:
firebase.getIteration(fbIterationID).then(function(result){
console.log('LOGGING FB ITERATION DOCUMENT WITH THE ID OF: ' + fbIterationID)
console.log(result)
})
You need to return the promise from getIteration so the caller can use its results. Making the function async isn't going to do that for you:
async function getIteration(id) {
return fb.db...
}
//gets the iteration with the given id
async function getIteration(id) {
return fb.db.collection('iteration').where('id', '==', id).get().then(snapshot => {
return snapshot.docs.map(doc => doc.data());
})
}
import React, {useEffect, useLayoutEffect, useState} from 'react'
import {View, Text, StyleSheet, SafeAreaView, ScrollView, TouchableOpacity } from 'react-native'
import { Avatar } from 'react-native-elements/dist/avatar/Avatar';
import CustomListItem from '../../components/CustomListItem'
import { auth, db } from '../../services/firebase';
import {SimpleLineIcons} from '#expo/vector-icons'
export default function HomeScreen({ navigation }) {
const [chats, setChats] = useState([]);
const [userInfo, setUserInfo] = useState([])
const [isDms, setIsDms] = useState(true);
useEffect(() => {
function unsubscribeDms(){
db.collection('chats').onSnapshot(snapshot => {
snapshot.docs.map(doc => {
if (doc.data().isDM == true){
if (doc.data().users[0] == auth.currentUser.uid){
setChats(chats => [...chats, {id: doc.id, data: doc.data(), otherUser: doc.data().users[1]}])
db.collection("users").doc(doc.data().users[1]).get().then((doc) => {
setUserInfo(userInfo => [...userInfo, {uid: doc.id, data: doc.data()}]
)
})
}
else if (doc.data().users[1] == auth.currentUser.uid){
setChats(chats => [...chats, {id: doc.id, data: doc.data(), otherUser: doc.data().users[0]}])
db.collection("users").doc(doc.data().users[0]).get().then((doc) => {
setUserInfo(userInfo => [...userInfo, {uid: doc.id, data: doc.data()}]
)
})
}
}
})
})}
async function unsubscribeGcs(){
await db.collection('chats').onSnapshot(snapshot => {
snapshot.docs.forEach(doc => {
if (doc.data().isDM == false){
var allUsers = doc.data().users
if (allUsers.includes(auth.currentUser.uid)){
setChats(chats => [...chats, {id: doc.id, data: doc.data()}])
}
}
})
})
}
if (isDms){
unsubscribeDms()
}
else {
unsubscribeGcs()
}
}, [])
function getphotoUrl(uid){
//return JSON.stringify(userInfo[0])
return userInfo.length
if (userInfo.length > 0){
var picInfo = userInfo.filter((value) =>{
return value.uid == uid
})[0].uid
//})[0].data.photoURL
return picInfo
}
else {
return 'null'
}
}
function getDisplayName(uid){
//return JSON.stringify(userInfo[0])
return userInfo.length
if (userInfo.length > 0){
var nameInfo = userInfo.filter((value) =>{
return value.uid == uid
})[0].uid
//})[0].data.displayName
console.log("this is the display name" + nameInfo)
return nameInfo
}
else {
return 'null'
}
}
function signOutUser(){
auth.signOut().then(() => {
navigation.replace('Login')
})
}
useLayoutEffect(() => {
navigation.setOptions({
title: "Chats",
headerRight: () => (
<View style={{
flexDirection: "row",
justifyContent: "space-between",
width: 80,
marginRight: 20
}}>
<View style={{marginLeft: 20}}>
<TouchableOpacity onPress={() => navigation.navigate("Profile", {currentUser: auth.currentUser})} activeOpacity={0.5}>
<Avatar rounded source={{ uri: auth?.currentUser?.photoURL }}/>
</TouchableOpacity>
</View>
<TouchableOpacity onPress={() => navigation.navigate("AddChat")}>
<SimpleLineIcons name="pencil" size={24} color="black"></SimpleLineIcons>
</TouchableOpacity>
</View>
)
});
}, [navigation])
function enterChat(id, chatName, photo){
navigation.navigate("Chat", {
id: id,
chatName: chatName,
photo: photo
})
}
return (
<SafeAreaView>
<ScrollView style={styles.container}>
{chats.map(({id, data, otherUser}) => (
<CustomListItem key={id} id={id} enterChat={enterChat} photo={getphotoUrl(otherUser)} userName={getDisplayName(otherUser)} isDm={data.isDM}/>
))}
</ScrollView>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
height: "100%"
}
})
I am trying to fetch some Data from an API and put them in a flatList. Each row in the flatList has a white star button which when pressing it would make this entry added to the favorites that are saved in AsyncStorage. What I want to do is to get the keys that are saved locally and check whether this item is in the favorites list I want to show a black star. I am not able to fill the favorites array in the state.
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* #format
* #flow
*/
import React, { Component } from 'react';
import {
StyleSheet, Text, View, ActivityIndicator, AsyncStorage,
Image, ToastAndroid, TouchableOpacity, FlatList
} from 'react-native';
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
isLoadingData: true,
dataSourceEmpty: null,
favorites: null
}
}
componentDidMount() {
return fetch('http://barcelonaapi.marcpous.com/bicing/stations.json')
.then((response) => response.json())
.then((JsonResponse) => {
this.setState({
isLoadingData: false,
dataSourceEmpty: JsonResponse.data.bici,
favorites: null
})
})
.catch((error) => {
console.log(error);
});
}
_touchListener = (item) => {
alert("ID is:" + item.id + "\n"
+ "Latitude is: " + item.lat + "\n"
+ "Longitude is:" + item.lon)
};
makeFavorite(item) {
this.saveData(item);
ToastAndroid.show(
'This station has been added to favorites!',
ToastAndroid.SHORT
);
}
saveData(station) {
AsyncStorage.setItem(station.id + "", station.name);
}
DATABASE_getAllBusStations = () => {
return new Promise(async (resolve, reject) => {
try {
let keys = await AsyncStorage.getAllKeys();
resolve(keys)
} catch (error) {
reject(new Error('Error getting keys from AsyncStorage: ' +
error.message))
}
});
}
checkifInDatabase = async () => {
try {
var keys = await this.DATABASE_getAllBusStations();
this.setState({
isLoadingData: true,
dataSourceEmpty: null,
favorites: keys
})
} catch (error) {
console.log(error);
}
}
render() {
if (this.state.isLoadingData) {
return (
<View style={styles.container}>
<ActivityIndicator size="large" animating />
</View>
);
}
else {
return (
<View style={styles.container}>
<Text style={styles.header}>
BARCELONA BUS STATIONS
</Text>
<FlatList
data={this.state.dataSourceEmpty}
renderItem={({ item }) => {
let source = './Assets/fav.png';
// if(this.state.favorites.includes(item.id))
// {
// source = './Assets/favblack.png';
// }
return <TouchableOpacity style={styles.item}
onPress={() => this._touchListener(item)}>
<View style={styles.row}>
<Text style={styles.textStyle}>
{item.name}
</Text>
<View style={styles.buttonStyle}>
<TouchableOpacity onPress=
{() => this.makeFavorite(item)}>
<Image
source={require(source)}
style={styles.imageStyle}
/>
</TouchableOpacity>
</View>
</View>
</TouchableOpacity>
}
}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
indicator: {
alignItems: 'center',
justifyContent: 'center',
},
row:
{
flexDirection: 'row',
justifyContent: 'space-between'
},
item: {
padding: 10,
borderBottomWidth: 1,
borderBottomColor: '#eee',
backgroundColor: 'skyblue'
},
header:
{
backgroundColor: '#F03209',
textAlign: 'center',
padding: 10,
color: 'skyblue',
fontSize: 20
},
imageStyle:
{
width: 50,
height: 50
},
textStyle:
{
padding: 10
}
});
You can either nest your calls or move them to an async function. See my example below.
import * as React from 'react';
import { RouteComponentProps } from 'react-router-dom';
interface ITestComponentState {
message_1: string;
message_2: string;
message_3: string;
message_4: string;
}
export default class TestComponent extends React.Component<RouteComponentProps<{}>, ITestComponentState> {
renders = 0;
constructor(props: any) {
super(props);
this.state = {
message_1: null,
message_2: null,
message_3: null,
message_4: null
}
}
componentDidMount() {
/**Using nested fetch calls**/
fetch('api/Local/GetData')
.then(response => response.text())
.then(message_1 => {
fetch('api/Local/GetData')
.then(response => response.text())
.then(message_2 => {
this.setState({ message_1, message_2 });
});
});
/**Using an async function**/
this.getMessages().then((messages: string[]) => this.setState({ message_3: messages[0], message_4: messages[1] }));
}
async getMessages() {
let message_3 = await fetch('api/Local/GetData').then(response => response.text());
let message_4 = await fetch('api/Local/GetData').then(response => response.text());
return [message_3, message_4];
}
public render() {
const { message_1, message_2, message_3, message_4 } = this.state;
this.renders++;
return (
<div>
<h1 className="test">Message 1: {message_1}</h1>
<h1 className="test">Message 2: {message_2}</h1>
<h1 className="test">Message 3: {message_3}</h1>
<h1 className="test">Message 4: {message_4}</h1>
<h2 className="test">Renders: {this.renders}</h2>
</div>
);
}
}
This one nests a fetch inside the first fetch.
fetch('api/Local/GetData')
.then(response => response.text())
.then(message_1 => {
fetch('api/Local/GetData')
.then(response => response.text())
.then(message_2 => {
this.setState({ message_1, message_2 });
});
});
This one puts them in an async method and calls the method inside componentDidMount.
this.getMessages().then((messages: string[]) => this.setState({ message_3: messages[0], message_4: messages[1] }));
async getMessages() {
let message_3 = await fetch('api/Local/GetData').then(response => response.text());
let message_4 = await fetch('api/Local/GetData').then(response => response.text());
return [message_3, message_4];
}
the results are:
Message 1: "Hello World!"
Message 2: "Hello World!"
Message 3: "Hello World!"
Message 4: "Hello World!"
Renders: 3
what about making the componentDidMount itself async?
async componentDidMount() {
try{
const response = await axios.get('http://barcelonaapi.marcpous.com/bicing/stations.json')
if(response.data.success){
this.setState({
isLoadingData: false,
dataSourceEmpty: response.data.bici,
favorites: null
})
}catch(error){
console.log(error);
}
}
I have a screen where the user selects the number of players for a game:
On the next screen, some text inputs appear and I want to be able to name the players so these names can be stored in a database:
I am using a FlatList to generate these fields, but it seems that I can't use the onChangeText here. If I try to input text into any of the fields I get this error:
This is the code:
import React, {Component} from 'react';
import {View, Text, StyleSheet, ScrollView, Image, FlatList} from 'react-native';
import colors from '../config/colors';
import {TextField} from '../components/TextField';
import {PrimaryButton} from '../components/PrimaryButton';
import AsyncStorage from '#react-native-community/async-storage';
class player_names_screen extends Component {
constructor (props) {
super(props);
this.state = {
num_players: [],
Player1: '',
Player2: '',
Player3: '',
Player4: '',
Player5: '',
Player6: '',
Player7: '',
Player8: ''
}
}
componentDidMount = () => {
this.getNumPlayers();
}
//This is my nasty way of generating the correct amount of text fields based on the number selected
getNumPlayers = async () => {
let players = await AsyncStorage.getItem('Num_Players');
this.setState({number_players: players});
var players_json_middle = '';
var x;
for (x = 1; x <= players; x++) {
player = '{"Player": "Player ';
num_str = x.toString();
if (x != players) {
var player_num = player.concat(num_str, '"},');
} else {
var player_num = player.concat(num_str, '"}');
}
players_json_middle = players_json_middle.concat(player_num);
}
var players_json_start = '{"Players":[';
var players_json_end = ']}';
var players_json_str = players_json_start.concat(players_json_middle, players_json_end);
var players_json = JSON.parse(players_json_str);
this.setState({num_players: players_json.Players});
}
renderItem = ({item}) => {
return (
<TextField
placeholder={item.Player}
//This is my main problem. It isn't the way to change the state of the player names.
onChangeText={() => this.setState(item.Player)}
/>
)
}
signUp = async () => {
let user = await AsyncStorage.getItem('email');
const {Player1} = this.state;
const {Player2} = this.state;
const {Player3} = this.state;
const {Player4} = this.state;
const {Player5} = this.state;
const {Player6} = this.state;
const {Player7} = this.state;
const {Player8} = this.state;
fetch('fetch address is here [this isn't the problem]', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application.json',
},
body: JSON.stringify({
email: user,
player1: Player1,
player2: Player2,
player3: Player3,
player4: Player4,
player5: Player5,
player6: Player6,
player7: Player7,
player8: Player8
})
}).then((response) => response.json())
.then((responseJson) => {
this.props.navigation.navigate('round_start_screen');
}).catch((error) => {
console.error(error);
});
}
render() {
return (
<ScrollView>
<View style={{alignItems: 'center'}}>
<Text style={styles.headingText}>
Game Name
</Text>
</View>
<View style={styles.container}>
<Text style={styles.text}>
What are the players' names?
</Text>
<View style={{width: '80%'}}>
//Rendering the flatlist here
<FlatList
data={this.state.num_players}
renderItem={this.renderItem}
keyExtractor={(item, index) => index.toString()}
/>
</View>
<PrimaryButton
onPress={() => this.signUp()}
label="Start Game"
>
</PrimaryButton>
</View>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
backgroundColor: colors.backgroundColor,
margin: 10,
paddingBottom: 5,
borderWidth: 1,
borderColor: colors.borderColor,
},
text: {
fontSize: 24,
color: colors.primaryText,
marginTop: 10,
},
headingText: {
fontSize: 24,
fontWeight: '500',
color: colors.primaryText,
margin: 10,
},
})
export default player_names_screen;
Is there a way to be able to name the players here and store them? Or is this whole approach an exercise in futility? Is there a much better way to do it?
You havent provided a key to update. You also need to use the text value that changed.
renderItem = ({item, index}) => {
return (
<TextField
placeholder={item.Player}
onChangeText={(newText) => this.setState({[`Player${index}`]: newText})
/>
)
}
You might want to consider using an array for this state though. It would enable you to update the array at the index position, instead of doing string interpolation.
If you need a fast solution, you can add a switch case as follows,
renderItem = ({item, index}) => {
return (
<TextField
placeholder={item.Player}
onChangeText={() => this._updateState(item, index)}
/>
)
}
_updateState = (item, index) => {
switch(index) {
case 0: this.setState({Player1 :item.Player}); break;
case 1: this.setState({Player2 :item.Player}); break;
case 2: this.setState({Player3 :item.Player}); break;
case 3: this.setState({Player4 :item.Player}); break;
case 4: this.setState({Player5 :item.Player}); break;
case 5: this.setState({Player6 :item.Player}); break;
case 6: this.setState({Player7 :item.Player}); break;
case 7: this.setState({Player8 :item.Player}); break
}
}