How to render lists using React Native and Firestore - firebase

I'm doing Expo React Native Application using Firestore Database. I'm following the below Tutorial.
Tutorial
function ViewServices () {
const navigation =useNavigation();
const [FullData, setFullData] = useState([]);
const [driverData, setDriverData] = useState([]);
// Retrieving Data
useEffect(() => {
firebase.firestore().collection('driver')
.get()
.then(snapshot => {
if (snapshot.empty) {
console.log('Empty');
return;
}
snapshot.forEach(doc => {
driverData.push(
{
id: doc.id,
});
});
{
driverData? driverData.map((point) => (
firebase.firestore().collection('driver')
.doc(point.id)
.collection('personal')
.get()
.then(snapshot => {
if (snapshot.empty) {
alert('Nothing To Show');
return;
}
snapshot.forEach(doc => {
FullData.push(
{
id : doc.id,
name: (doc.data().firstname),
school: (doc.data().lastname),
});
});
setFullData(FullData);
})
.catch(err => {
console.log('Error getting documents', err);
})
))
: null}
})
.catch(err => {
console.log('Error getting documents', err);
});
}, []);
return(
<FlatList
data={FullData}
renderItem={({ item }) => (
<View style={{ height: 50, flex: 1, alignItems: 'center',
justifyContent: 'center' }}>
<Text>User ID: {item.id}</Text>
<Text>User Name: {item.name}</Text>
</View>
)}
/>
)}
However the data are retrieved fine. But the lists are only shown when I manually press Refresh (Ctrl+S)
How can I retrieve the lists when the screen loads? Please let me know.

I found a solution.
The problem happened because the components are firstly rendered before the data fetching. That's why it required manual refresh.
I have added Activity Indicator component and it will be loaded until the data fetched. After the data successfully fetched, activity indicator will be replaced with flat list component.
function ViewServices () {
**const [loading, setLoading] = useState(true)**
const navigation =useNavigation();
const [FullData, setFullData] = useState([]);
const [driverData, setDriverData] = useState([]);
// Retrieving Data
useEffect(() => {
firebase.firestore().collection('driver')
.get()
.then(snapshot => {
if (snapshot.empty) {
console.log('Empty');
return;
}
snapshot.forEach(doc => {
driverData.push(
{
id: doc.id,
});
});
{
driverData? driverData.map((point) => (
firebase.firestore().collection('driver')
.doc(point.id)
.collection('personal')
.get()
.then(snapshot => {
if (snapshot.empty) {
alert('Nothing To Show');
return;
}
snapshot.forEach(doc => {
FullData.push(
{
id : doc.id,
name: (doc.data().firstname),
school: (doc.data().lastname),
});
});
setFullData(FullData);
**setLoading(false);**
})
.catch(err => {
console.log('Error getting documents', err);
})
))
: null}
})
.catch(err => {
console.log('Error getting documents', err);
});
}, []);
**if (loading) {
return (
<ActivityIndicator size="large" color="#00ff00" justifyContent= "center"/>
)
}**
else{
return(
<FlatList
data={FullData}
renderItem={({ item }) => (
<View style={{ height: 50, flex: 1, alignItems: 'center',
justifyContent: 'center' }}>
<Text>User ID: {item.id}</Text>
<Text>User Name: {item.name}</Text>
</View>
)}
/>
)
}
}

Related

get lat and long from all firebase documents

I am trying to get location from all documents of my collection, but in this way I am getting just one document lat and long.
I need to get all lat and long data from all documents and show it in my map
what I am doing wrong??
can anyone help me with it??
POINTS STATE:
const [points, setPoints] = useState([]);
useEffect(() => {
async function getProviderLocation() {
try {
const db = firestore();
await db
.collection('Providers')
.get()
.then(snapshot => {
if (snapshot.empty) {
console.log('nao tem');
return;
}
console.log('docss', snapshot.docs);
snapshot.forEach(async doc => {
// console.log('ID', doc.id);
setPoints([
...points,
{
id: doc.id,
latitude: doc.data().address.latitude,
longitude: doc.data().address.longitude,
},
]);
console.log('points', points);
});
})
.catch(err => {
console.log('Error getting documents', err);
});
} catch (error) {
console.log(error);
}
}
if (coordinates) getProviderLocation();
}, []);
RENDERING:
{points.map(point => (
<Marker
onPress={() => navigateToCreateAppointment(point.id)}
style={styles.mapMarkerImage}
key={point.id}
coordinate={{
latitude: parseFloat(point.latitude),
longitude: parseFloat(point.longitude),
latitudeDelta: 0.015,
longitudeDelta: 0.0121,
}}
>
<Image source={wingImg} />
</Marker>
))}

TypeError: undefined is not an object (evaluating 'const.join')

How to declare an array on a state variable
Im using react native expo and firebase all is up to date
export default class Profile extends Component {
state = {
ageRangeValues: this.props.user.ageRange,
distanceValue: [this.props.user.distance],
}
render() {
const {
ageRangeValues,
distanceValue,
} = this.state;
return (
<View>
<Slider
min={5}
max={100}
values={distanceValue}
onValuesChange={val => this.setState({ distanceValue: val })}
onValuesChangeFinish={val => this.updateUser('distance', val[0])}
/>
<Slider
min={18}
max={70}
values={ageRangeValues}
onValuesChange={val => this.setState({ ageRangeValues: val })}
onValuesChangeFinish={val => this.updateUser('ageRange', val)}
/>
</View>) }
I expect this to work fine but the ageRangeValue is undefined but the distanceValue in defined don't know why may be is because ageRangeValue takes ageRange and its an Array. If I declare areRangeValue: [19, 20], everything works, but if I left it the way it is all my values are undefined
and here is my preload
const firebaseConfig = {
apiKey: 'XXXXXXXXX',
databaseURL: 'XXXXX',
storageBucket: 'XXXXX',
};
firebase.initializeApp(firebaseConfig);
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
width: null,
height: null,
resizeMode: 'contain',
},
});
export default class Preload extends Component {
constructor() {
super();
this.loadApp();
// SVGAnimatedLengthList.loadApp();
}
authenticate = (token) => {
const provider = firebase.auth.FacebookAuthProvider
const credential = provider.credential(token);
return firebase.auth().signInWithCredential(credential);
};
_goHome = (user) => {
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Home', params: { user } })],
});
this.props.navigation.dispatch(resetAction);
};
loadApp = async () => {
//firebase.auth().signOut();
firebase.auth().onAuthStateChanged((auth) => {
if (auth) {
this.firebaseRef = firebase.database().ref('users');
this.firebaseRef.child(auth.uid).on('value', (snap) => {
const user = firebase.auth().currentUser;
if (user != null) {
this.firebaseRef.child(auth.uid).off('value');
this._goHome(user);
}
});
} else {
this.setState({ showSpinner: false });
this.props.navigation.navigate('Login');
}
});
}
render() {
return (
<ImageBackground source={require('./images/fondo.png')} style={styles.container}>
<ActivityIndicator />
</ImageBackground>
);
}
}
Try it with constructor
export default class Profile extends Component {
constructor(){
super();
this.state = {
ageRangeValues: this.props.user.ageRange,
distanceValue: [this.props.user.distance],
};
}
render() {
const { ageRangeValues, distanceValue } = this.state;
return (
<View>
<Slider
min={5}
max={100}
values={distanceValue}
onValuesChange={val => this.setState({ distanceValue: val })}
onValuesChangeFinish={val => this.updateUser('distance', val[0])}
/>
<Slider
min={18}
max={70}
values={ageRangeValues}
onValuesChange={val => this.setState({ ageRangeValues: val })}
onValuesChangeFinish={val => this.updateUser('ageRange', val)}
/>
</View>
);
}
Vencovsky was right on the previews page that pass the data
loadApp = async () => {
//firebase.auth().signOut();
firebase.auth().onAuthStateChanged((auth) => {
if (auth) {
this.firebaseRef = firebase.database().ref('users');
this.firebaseRef.child(auth.uid).on('value', (snap) => {
const user = firebase.auth().currentUser;
if (user != null) {
this.firebaseRef.child(auth.uid).off('value');
this._goHome(user);
}
});
} else {
this.setState({ showSpinner: false });
this.props.navigation.navigate('Login');
}
});
}
Changing const user = firebase.auth().currentUser; to const user = snap.val();
made the trick

RNFetchBlob is not working to upload image

I am trying to make a upload function for my Firebase Cloud Storage. I am using RNFetchBlob, along with react-native-image-picker. I can select photos, but it will not upload. All other Firebase functions works great and it is installed through React-Native-Firebase...
Nothing seems to happen after 'fs.readFile'.
import React, { Component } from 'react'
import {
StyleSheet,
Text,
View,
TouchableOpacity,
Platform,
Image,
ActivityIndicator
} from 'react-native'
import ImagePicker from 'react-native-image-picker'
import RNFetchBlob from 'rn-fetch-blob'
import firebase from 'react-native-firebase';
const storage = firebase.storage()
// Prepare Blob support
const Blob = RNFetchBlob.polyfill.Blob
const fs = RNFetchBlob.fs
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest
window.Blob = Blob
const uploadImage = (uri, mime = 'application/octet-stream') => {
return new Promise((resolve, reject) => {
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri
const sessionId = new Date().getTime()
let uploadBlob = null
const imageRef = storage.ref('images').child('${sessionId}')
fs.readFile(uploadUri, 'base64')
.then((data) => {
return Blob.build(data, { type: '${mime};BASE64' })
})
.then((blob) => {
uploadBlob = blob
return imageRef.put(blob, { contentType: mime })
})
.then(() => {
uploadBlob.close()
return imageRef.getDownloadURL()
})
.then((url) => {
resolve(url)
})
.catch((error) => {
reject(error)
})
})
}
class Demo extends Component {
constructor(props) {
super(props)
this.state = {}
}
_pickImage() {
this.setState({ uploadURL: '' })
ImagePicker.launchImageLibrary({}, response => {
uploadImage(response.uri)
.then(url => this.setState({ uploadURL: url }))
.catch(error => console.log(error))
})
}
render() {
return (
<View style={ styles.container }>
{
(() => {
switch (this.state.uploadURL) {
case null:
return null
case '':
return <ActivityIndicator />
default:
return (
<View>
<Image
source={{ uri: this.state.uploadURL }}
style={ styles.image }
/>
<Text>{ this.state.uploadURL } {this.state.uploadURL}</Text>
</View>
)
}
})()
}
<TouchableOpacity onPress={ () => this._pickImage() }>
<Text style={ styles.upload }>
Upload
</Text>
</TouchableOpacity>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
image: {
height: 200,
resizeMode: 'contain',
},
upload: {
textAlign: 'center',
color: '#333333',
padding: 10,
marginBottom: 5,
borderWidth: 1,
borderColor: 'gray'
},
})
export default Demo
I get this error in my console when selecting a photo:
filePath.replace is not a function. (In 'filePath.replace('file://',
'')', 'filePath.replace' is undefined)]
Just pass the image url directly to storage ref. You don't need to create a blob file anymore. I guess firebase handles it internally now.
Here an example i just tested together with react-native-image-crop-picker:
import firebase from 'react-native-firebase';
import ImagePicker from 'react-native-image-crop-picker';
export default class ImageUploadService {
static init() {}
static openPickerAndUploadImage() {
const uid = '12345';
ImagePicker.openPicker({
width: 300,
height: 300,
cropping: true,
mediaType: 'photo',
})
.then(image => {
const imagePath = image.path;
const imageRef = firebase
.storage()
.ref(uid)
.child('dp.jpg');
let mime = 'image/jpg';
imageRef
.put(imagePath, { contentType: mime })
.then(() => {
return imageRef.getDownloadURL();
})
.then(url => {
// You could now update your users avatar for example
//firebase.database().ref('users').child(uid).update({ ...userData})
console.log('URL', url);
});
})
.catch(error => {
console.log(error);
});
}
}
ImageUploadService.init();
Now just call ImageUploadService.openopenPickerAndUploadImage() in one of your components.
I am sure this will also work with react-native-image-picker too, just remove the blob parts in your code and pass the image url directly to your imageRef.put
==>
const uploadImage = (uri, mime = 'application/octet-stream') => {
return new Promise((resolve, reject) => {
const imagePath = uri;
const imageRef = firebase
.storage()
.ref('images')
.child('dp.jpg');
let mime = 'image/jpg';
imageRef
.put(imagePath, { contentType: mime })
.then(() => {
return imageRef.getDownloadURL();
})
.then(resolve)
.catch(reject);
});
};

React Native / Firebase - upload image with additional info

I used this block of code to select an image when button is pressed to upload to firebase.
<TouchableHighlight onPress={ () => this._pickImage() }
style={styles.button}>
<Text
style={styles.buttonText}>
Select a Photo
</Text>
</TouchableHighlight>
_pickImage() {
this.setState({ uploadURL: '' })
ImagePicker.launchImageLibrary({}, response => {
uploadImage(response.uri)
.then(url => this.setState({ uploadURL: url }))
.catch(error => console.log(error))
});
}// end _pickImage
const uploadImage = (uri, mime = 'application/octet-stream') => {
return new Promise((resolve, reject) => {
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri
const sessionId = new Date().getTime()
let uploadBlob = null
const imageRef = storage.ref('images').child(`${sessionId}`)
fs.readFile(uploadUri, 'base64')
.then((data) => {
return Blob.build(data, { type: `${mime};BASE64` })
})
.then((blob) => {
uploadBlob = blob
return imageRef.put(blob, { contentType: mime })
})
.then(() => {
uploadBlob.close()
return imageRef.getDownloadURL()
})
.then((url) => {
resolve(url)
})
.catch((error) => {
reject(error)
})
})
}
This works fine. How can I change this to let the user select photo and then hit another button store additional info for the image like Description entered by the user into the same data structure in firebase?

react native: run spinner until ListView rendering

in my app I pull data from Firebase and render them in a ListView. I want that the spinner will run until the entire list will appear.
I added the a view with a conditions that show if the state.loading is 'true', but if I change it in componentDidMount function does not work because the list is not yet displayed
Here is my code:
module.exports = React.createClass({
getInitialState() {
return({
loading: false,
displayName: '',
title: '',
dataSource: ds.cloneWithRows([{
title: '',
author: ''
}])
})
},
componentDidMount() {
let user = firebaseApp.auth().currentUser;
if (!user.displayName) {
this.props.navigator.push({
name: 'chooseName'
})
} else {
// proceed normally with application
this.setState({
displayName: user.displayName
})
this.listenForItems(topicsRef);
}
},
listenForItems(ref) {
ref.on('value', (snap) => {
let topics = [];
snap.forEach(topic => {
topics.push({
title: topic.val().title,
author: topic.val().author,
key: topic.key
})
})
this.setState({dataSource: ds.cloneWithRows(topics)});
})
},
signOut() {
// sign out the user
firebaseApp.auth().signOut()
.then(() => {
// Sign out successful
this.props.navigator.popToTop();
}, (error) => {
console.log(error);
})
},
details(data) {
this.props.navigator.push({
name: 'topicDetail',
displayName: this.state.displayName,
title: data.title,
author: data.author,
row_uid: data.key
})
},
renderRow(rowData) {
return (
<TouchableOpacity style={styles.row}
onPress={() => this.details(rowData)}
>
<Text style={styles.rowTitle}>
{rowData.title}
</Text>
<Text>
{rowData.author}
</Text>
</TouchableOpacity>
)
},
addTopic() {
topicsRef.push({
title: this.state.title,
author: this.state.displayName
})
},
render() {
if (this.state.loading) {
return (
<Container style={styles.containerSignIn}>
<Content>
<Spinner />
</Content>
</Container>);
}
return (
<View style={styles.flexContainer}>
<View style={styles.header}>
<TouchableOpacity
onPress={() => this.signOut()}
>
<Text style={styles.link}>
Sign out
</Text>
</TouchableOpacity>
<Text style={styles.title}>
{this.state.displayName}
</Text>
</View>
<View style={styles.body}>
<TextInput
placeholder='Something on your mind?'
style={styles.input}
onChangeText={(text) => this.setState({title: text})}
onEndEditing={() => this.addTopic()}
/>
<ListView
style={styles.list}
enableEmptySections={true}
dataSource={this.state.dataSource}
renderRow={(rowData) => this.renderRow(rowData)}
/>
</View>
</View>
)
}
});
I assume your Spinner component works.
Due to possible issues in your react lifecycle what I can recommend in the first run:
1.) if you want your Spinner to be active when your View shows up, set state.loading to true when defining your initialState.
getInitialState() {
return({
loading: true,
...
})
}
2.) change the loading state in the (success) Promise callback of your firebase request and NOT in componentDidMount().
listenForItems(ref) {
ref.on('value', (snap) => {
...
this.setState({
loading: false,
dataSource: ds.cloneWithRows(topics)
});
})
}
Hope that helps.

Resources