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);
}
}
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'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>
)
}
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 have a variable in state called isLoading. The idea is to display a loading message while the program is communicating the server, then display the data. However, at ling 24, I get an error:
TypeError: This.setState is not a function (in 'this.setState({ isloadin: false});
import React from "react";
import { StyleSheet, Text, View, AsyncStorage } from "react-native";
var text;
export default class App extends React.Component {
constructor(props) {
super(props);
state = {
isLoading: true
};
}
componentDidMount = () => {
AsyncStorage.getItem("accessToken").then(token => {
postdata(
"http://1.0.0.0:1337/loadTransactions",
{ UserID: 69 },
function(result) {
text = toString(result.Data[1].ID);
text = result.Data[1].Label;
console.log(result.Data[1].Label);
this.setState({
isLoading: false
});
}
);
});
};
render() {
console.log(this.setState.isLoading);
if (this.setState.isLoading) {
console.log(this.setState.isLoading);
return (
<View style={styles.container}>
<Text>Loading....</Text>
</View>
);
} else {
return (
<View style={styles.container}>
<Text>Hi, {text}</Text>
<Text>Test</Text>
</View>
);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center"
}
});
To maintain the context of a function as the same context where the function was lexically defined, you have to use an arrow function:
componentDidMount = () => {
AsyncStorage.getItem("accessToken").then(token => {
postdata(
"http://204.48.23.161:1337/loadTransactions",
{ UserID: 69 },
function(result) {
// ^^^^^^^ use `result => ` here
text = toString(result.Data[1].ID);
text = result.Data[1].Label;
console.log(result.Data[1].Label);
this.setState({
isLoading: false
});
}
);
});
};
this (ref to the instance of class) might not be available inside the context of AsyncStorage. Save this as another variable and use inside:
componentDidMount = () => {
const self = this;
AsyncStorage.getItem("accessToken").then(token => {
postdata(
"http://204.48.23.161:1337/loadTransactions",
{ UserID: 69 },
function(result) {
text = toString(result.Data[1].ID);
text = result.Data[1].Label;
console.log(result.Data[1].Label);
self.setState({
isLoading: false
});
}
);
});
};
if i write this onPress={() => this.submit()} instead onPress={() => this.onSendBtnPressed()} i can get the text value but if i just write onPress={() => this.onSendBtnPressed()} this i can't see the text on the message list
here is my chatui
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {KeyboardAvoidingView, Image, TouchableOpacity, ReactNative,
StyleSheet } from 'react-native';
import {Footer, FooterTab} from "native-base";
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-
view';
import { View, Title, Screen, ScrollView, TextInput,Text } from
'#shoutem/ui';
import Messages from '../containers/Messages';
import Input from '../containers/Input';
import { sendMessage } from '../actions';
const mapStateToProps = (state) => ({
chatHeight: state.chatroom.meta.height,
user: state.user,
});
class ChatUI extends Component {
constructor()
{
super();
this.state = {
text: '',
scrollViewHeight: 0,
inputHeight: 0
}
}
updateValue(text) {
console.warn(text)
}
componentDidMount() {
this.scrollToBottom(false);
}
componentDidUpdate() {
this.scrollToBottom();
}
onChangeText(text) {
this.setState({text: text})
};
onSendBtnPressed (text) {
return sendMessage(text, this.props.user)
this.textInput.clear();
Keyboard.dismiss();
}
onScrollViewLayout = (event) => {
const layout = event.nativeEvent.layout;
this.setState({
scrollViewHeight: layout.height
});
}
onInputLayout = (event) => {
const layout = event.nativeEvent.layout;
this.setState({
inputHeight: layout.height
});
}
scrollToBottom(animate = false) {
const { scrollViewHeight, inputHeight } = this.state,
{ chatHeight } = this.props;
const scrollTo = chatHeight - scrollViewHeight + inputHeight;
if (scrollTo > 0) {
this.refs.scroll.scrollToEnd();
}
}
_scrollToInput(reactRef) {
this.refs.scroll.scrollToFocusedInput(ReactNative.findNodeHandle(reactRef));
}
submit() {
let collection = {}
collection.text = this.state.text,
console.warn(collection);
}
render() {
return (
<Screen >
<Title style={{paddingTop: 25, paddingLeft: 10, borderBottomWidth: 0.5, backgroundColor: 'white', paddingBottom: 10}}>
<Image style={{width:80, height: 80}} source={require('../img/11.png')} />
<Text> GENERAL CHAT</Text>
</Title>
<KeyboardAwareScrollView ref="scroll"
onLayout={this.onScrollViewLayout}>
<Messages />
</KeyboardAwareScrollView>
<Footer style={{width:360,
height:30,
backgroundColor:'#fff',
borderRadius:20,
borderWidth: 0.5,
marginBottom: 3,
borderColor: 'gray',
}}>
<TextInput
onSubmitEditing = {this.onSubmitEditing}
multiline
onLayout={this.onInputLayout}
submitAction={this.onSendBtnPressed}
ref="input"
placeholder="Say something ..."
onChangeText={(text) => this.onChangeText(text, 'text')}
style={{backgroundColor: 'rgba(0,0,0,0)',
borderBottomWidth: 1.5, borderColor: '#f8f8f8', fontSize: 16, color: 'gray', paddingBottom:20}}
ref={input => { this.textInput = input; } }/>
</Footer>
<TouchableOpacity
onPress={() => this.onSendBtnPressed()}
style={{marginLeft:280, backgroundColor:'gray', width: 70, height: 30}}
title= "send"
>
</TouchableOpacity>
</Screen>
)
}
}
export default connect(mapStateToProps, {sendMessage})(ChatUI);
and here is my actions
import firebase from '../firebase';
import DeviceInfo from 'react-native-device-info';
import {Actions} from 'react-native-router-flux';
import FCM, { FCMEvent, NotificationType, WillPresentNotificationResult,
RemoteNotificationResult } from 'react-native-fcm';
import { Platform } from 'react-native';
export const addMessage = (msg) => ({
type: 'ADD_MESSAGE',
...msg
});
export const sendMessage = (text, user) => {
return function (dispatch) {
let msg = {
text: text,
time: Date.now(),
author: {
name: user.name,
avatar: user.avatar
}
};
const newMsgRef = firebase.database()
.ref('messages')
.push();
msg.id = newMsgRef.key;
newMsgRef.set(msg);
dispatch(addMessage(msg));
};
};
export const startFetchingMessages = () => ({
type: 'START_FETCHING_MESSAGES'
});
export const receivedMessages = () => ({
type: 'RECEIVED_MESSAGES',
receivedAt: Date.now()
});
export const fetchMessages = () => {
return function (dispatch) {
dispatch(startFetchingMessages());
firebase.database()
.ref('messages')
.orderByKey()
.limitToLast(20)
.on('value', (snapshot) => {
setTimeout(() => {
const messages = snapshot.val() || [];
dispatch(receiveMessages(messages))
}, 0);
});
}
}
export const receiveMessages = (messages) => {
return function (dispatch) {
Object.values(messages).forEach(msg => dispatch(addMessage(msg)));
dispatch(receivedMessages());
}
}
export const updateMessagesHeight = (event) => {
const layout = event.nativeEvent.layout;
return {
type: 'UPDATE_MESSAGES_HEIGHT',
height: layout.height
}
}
export const setUserName = (name) => {
return (dispatch) => {
dispatch({
type: 'SET_USER_NAME',
payload: name
});
};
};
export const setUserAvatar = (avatar) => ({
type: 'SET_USER_AVATAR',
avatar: avatar && avatar.length > 0 ? avatar : 'avatar.jpg'
});
export const login = () => {
return function (dispatch, getState) {
dispatch(startAuthorizing());
firebase.auth()
.signInAnonymously()
.then(() => {
const { name, avatar } = getState().user;
firebase.database()
.ref(`users/`)
.push({
name,
avatar
});
dispatch(userAuthorized());
dispatch(fetchMessages());
});
}
}
export const checkUserExists = () => {
return function (dispatch) {
dispatch(startAuthorizing());
firebase.auth()
.signInAnonymously()
.then(() => firebase.database()
.ref(`users/${DeviceInfo.getUniqueID()}`)
.once('value', (snapshot) => {
const val = snapshot.val();
if (val === null) {
dispatch(userNoExist());
}else{
dispatch(setUserName(val.name));
dispatch(setUserAvatar(val.avatar));
startChatting(dispatch);
}
}))
.catch(err => console.log(err))
}
}
const startChatting = function (dispatch) {
dispatch(userAuthorized());
dispatch(fetchMessages());
FCM.requestPermissions();
FCM.getFCMToken()
.then(token => {
console.log(token)
});
FCM.subscribeToTopic('secret-chatroom');
FCM.on(FCMEvent.Notification, async (notif) => {
console.log(notif);
if (Platform.OS === 'ios') {
switch (notif._notificationType) {
case NotificationType.Remote:
notif.finish(RemoteNotificationResult.NewData); //other
types available: RemoteNotificationResult.NewData,
RemoteNotificationResult.ResultFailed
break;
case NotificationType.NotificationResponse:
notif.finish();
break;
case NotificationType.WillPresent:
notif.finish(WillPresentNotificationResult.All); //other
types available: WillPresentNotificationResult.None
break;
}
}
});
FCM.on(FCMEvent.RefreshToken, token => {
console.log(token);
});
}
export const startAuthorizing = () => ({
type: 'USER_START_AUTHORIZING'
});
export const userAuthorized = () => ({
type: 'USER_AUTHORIZED'
});
export const userNoExist = () => ({
type: 'USER_NO_EXIST'
});
and MessageList
import React, { Component } from 'react';
import {
ListView, Text, Row, Image,
View, Subtitle, Caption, Heading
} from '#shoutem/ui';
import moment from 'moment';
const Message = ({ msg }) => (
<Row>
<Image styleName="small-avatar top"
source={{ uri: msg.author.avatar }} />
<View styleName="vertical">
<View styleName="horizontal space-between">
<Subtitle>{msg.author.name}</Subtitle>
<Caption>{moment(msg.time).from(Date.now())}</Caption>
</View>
<Text styleName="multiline">{msg.text}</Text>
</View>
</Row>
);
const MessageList = ({ messages, onLayout }) => (
<ListView data={messages}
autoHideHeader={true}
renderRow={msg => <Message msg={msg} />}
onLayout={onLayout}
/>
);
export default MessageList;
You are not passing a text variable into your onSendButtonPressed function. It looks like you should be calling it using the following syntax:
onPress={() => this.onSendBtnPressed(someText)}
Your onSendBtnPressed() function has the return statement on the first line, so the code will not be fired underneath the return statement (not related to your question, but hope to fix any other headaches...)