react-native-firebase firestore permission denied - firebase

What I'm trying to do here is to actually just read data from my firestore. I followed all the installation from RNF's docs, but when I try to get data from my documents or just reference into firestore collection, an error shows up:
I tried changing the rules (as I thought it was the rules involved) but no luck.
Here is my database structure:
And below are my codes :
import React, { Component } from 'react';
import {View, Text, FlatList, Image, StyleSheet, Button, TouchableHighlight} from 'react-native';
import firebase from 'react-native-firebase';
import MainApp from './src/screen/test';
export default class App extends Component<{}> {
constructor(){
super();
this.itemsRef = this.getRef('questions');
this.state = {
items:[],
loading:true,
};
}
setModalVisible(visible){
this.setState(modalVisible:visible);
}
getRef(location){
return firebase.firestore().collection('chat');
}
componentWillMount(){
this.getItems(this.itemsRef);
}
componentDidMount(){
//this.getItems(this.itemsRef);
}
getItems(itemsRef){
firebase.firestore().collection('Users').get().then((snap)=>{
let items =[];
alert(snap);
snap.forEach((childSnap)=>{
items.push({
title:childSnap.val().question,
_key:childSnap.key
});
alert(childSnap.key)
})
this.setState({
items,
loading:false
})
})
}
pressRow(item){
alert(item);
}
renderRow(item){
return(
<TouchableHighLight onPress={()=>{
this.pressRow(item)
}}>
<View>
<Text>{item.title}</Text>
</View>
</TouchableHighLight>
)
}
addItem(){
}
render() {
if (this.state.loading) {
return null; // or render a loading icon
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.state.items}
/>
<Button
title={'Add TODO'}
onPress={() => console.log()}
/>
</View>
);
}
}
How can I fix this error?

The default setting for firestore is to disable access.
So if you don't have your access rules set up, you can't fetch data.
Change them to this - but ONLY for testing:
service cloud.firestore {
match /databases/{database}/documents {
// Match all documents, recursively, with a wildcard and the "=**" recursive modifier
match /{document=**} {
allow read, write;
}
}
}
Then after it works - read more about here.

Related

React + Formik + Firebase/Firestore Uncaught Error in snapshot listener:, [FirebaseError: Missing or insufficient permissions.]

Making an instagram clone and it won't let me post a picture. I keep getting this error 'Uncaught Error in snapshot listener:, [FirebaseError: Missing or insufficient permissions.]' whenever I enter the page to upload a picture. My guess is that it's coming from the onSnapshot function. I also get the 'Warning: An unhandled error was caught from submitForm(), [TypeError: null is not an object (evaluating 'currentLoggedInUser.username')]' when I press the share button. Nothing happens beyond that point. Its supposed to take me back to the homescreen afterwards:
import { View, Text, Image, TextInput, Button } from 'react-native'
import React, { useState, useEffect } from 'react'
import * as Yup from 'yup'
import { Formik } from 'formik'
import { Divider } from 'react-native-elements'
import validUrl from 'valid-url'
import {db, firebase} from '../../firebase'
const PLACEHOLDER_IMG = 'https://pacificpatiostructures.com/wp-content/uploads/2016/06/import_placeholder.png'
const uploadPostSchema = Yup.object().shape({
imageUrl: Yup.string().url().required('A URL is required'),
caption: Yup.string().max(2200, 'Caption has reached the character limit.')
})
const FormikPostUploader = ({ navigation }) => {
const [thumbnailUrl, setThumbnailUrl] = useState(PLACEHOLDER_IMG)
const [currentLoggedInUser, setCurrentLoggedInUser] = useState(null)
const getUsername = () => {
const user = firebase.auth().currentUser
const unsubscribe = db
.collection('user')
.where('owner_uid', '==', 'user.uid').limit(1).onSnapshot(
snapshot => snapshot.docs.map(doc => {
setCurrentLoggedInUser({
username: doc.data().username,
profilePicture: doc.data().profile_picture,
})
})
)
return unsubscribe
}
useEffect(() => {
getUsername()
}, [])
const uploadPostToFirebase = (imageUrl, caption) => {
const unsubscribe = db
.collection('users')
.doc(firebase.auth().currentUser.email).collection('posts')
.add({
imageUrl: imageUrl,
user: currentLoggedInUser.username,
profile_picture: currentLoggedInUser.profilePicture,
owner_uid: firebase.auth().currentUser.uid,
caption: caption,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
likes: 0,
likes_by_users: [],
comments: [],
})
.then(() => navigation.goBack())
return unsubscribe
}
return (
<Formik
initialValues={{caption: '', imageUrl: ''}}
onSubmit={values => {
uploadPostToFirebase(values.imageUrl, values.caption)
}}
validationSchema={uploadPostSchema}
validateOnMount={true}
>
{({
handleBlur,
handleChange,
handleSubmit,
values,
errors,
isValid
}) => (
<>
<View
style={{
margin: 20,
justifyContent: 'space-between',
flexDirection: 'row',
}}>
<Image source={{ uri: validUrl.isUri(thumbnailUrl) ? thumbnailUrl : PLACEHOLDER_IMG}}
style={{ width: 100, height: 100 }}/>
<View style={{ flex: 1, marginLeft: 12 }}>
<TextInput
style={{ color: 'white', fontSize: 20 }}
placeholder='Write a caption...'
placeholderTextColor='gray'
multiline={true}
onChangeText={handleChange('caption')}
onBlur={handleBlur('caption')}
value={values.caption}
/>
</View>
</View>
<Divider width = {0.2} orientation='vertical' />
<TextInput
onChange={(e) => setThumbnailUrl(e.nativeEvent.text)}
style={{ color: 'white', fontSize: 18 }}
placeholder='Enter Image Url'
placeholderTextColor='gray'
onChangeText={handleChange('imageUrl')}
onBlur={handleBlur('imageUrl')}
value={values.imageUrl}
/>
{errors.imageUrl &&(
<Text style={{ fontSize: 10, color: 'red' }}>
{errors.imageUrl}
</Text>
)}
<Button onPress={handleSubmit} title='Share' disabled={!isValid}/>
</>
)}
</Formik>
)
}
export default FormikPostUploader
Here are my security rules that I used in Cloud Firestore:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function userIsAuthenticated() {
return request.auth != null;
}
// security rule for group collection ('post')
match /{path=**}/posts/{postId} {
allow read, write: if userIsAuthenticated();
}
match /users/{userId} {
allow read, write: if userIsAuthenticated();
}
}
}
Your getUsername() function queries user collection which is not included in your Firestore Rules which returns Missing or insufficient permissions.
As per this documentation:
Cloud Firestore security rules evaluate each query against its potential result and fails the request if it could return a document that the client does not have permission to read. Queries must follow the constraints set by your security rules.
You should also add necessary permissions to the user collection. See sample rule below:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function userIsAuthenticated() {
return request.auth != null;
}
// security rule for group collection ('post')
match /{path=**}/posts/{postId} {
allow read, write: if userIsAuthenticated();
}
match /users/{usersId} {
allow read, write: if userIsAuthenticated();
}
// Security Rule for `user` collection.
match /user/{userId} {
allow read, write: if userIsAuthenticated();
}
}
}

this.props.route.params returns value as undefined

I'm building a barcode reader app that scans that qr code and then takes data and is used as a key to fetch an object from firebase. In order the data to be used as a key I need to pass through another screen but when I check console log it's cameback that the scanned key is undefined.
The itself barcode scanner works perfectly.
Barcode class :
export class BarCodeScannerScreen extends Component{
state = {
CameraPermissionGranted: null,
}
async componentDidMount() {
// Ask for camera permission
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ CameraPermissionGranted: status === "granted" ? true : false });
};
barCodeScanned = ({ data }) => {
//Access the Data
alert(data); // shows the scanned key
this.props.navigation.navigate('Info', {
item: data, }); // but then it's dissapears in here.
};
render(){
const { CameraPermissionGranted } = this.state;
if(CameraPermissionGranted === null){
// Request Permission
return(
<View style={styles.container}>
<Text>Please grant Camera permission</Text>
</View>
);
}
if(CameraPermissionGranted === false){
// Permission denied
return (
<View style={styles.container}>
<Text>Camera Permission Denied.</Text>
</View>
);
}
if(CameraPermissionGranted === true){
// Got the permission, time to scan
return (
<View style = {{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<BarCodeScanner
onBarCodeScanned = {this.barCodeScanned }
style = {{
height: DEVICE_HEIGHT/1.1,
width: DEVICE_WIDTH,
}}
>
</BarCodeScanner>
</View>
);
}
}
}
Here is my Info screen that receives the information :
export default class InfoScreen extends Component {
constructor(props){
super(props);
this.state={
productlist:[],
scannedkey: this.props.route.params.item
} }
async componentDidMount(){
firebase.database().ref(`product/${ this.state.scannedkey}`).on(
"value",
(snapshot) => {
var list = [];
snapshot.forEach((child) => {
list.push({
key: child.key,
title: child.val().title,
//details: child.val().details,
//price: child.val().price
});
});
this.setState({ productlist: list });
},
(error) => console.error(error)
);
}
componentWillUnmount() {
if (this.valuelistener_) {
this.valueRef_.off("value", this.valuelistener_)
}}
render() {
console.log(this.state.scannedkey); // console log shows that scanned key is undefined
return(
<View style={styles.container}>
<Text>Hey</Text>
<Text>{this.state.productlist.title}</Text>
</View>
);}}
App.js
export default function App() {
const Drawer=createDrawerNavigator();
return (
<Provider store={store}>
<NavigationContainer>
<Drawer.Navigator initialRouteName="Barcode">
<Drawer.Screen name="Barcode" component={BarCodeScannerScreen} />
<Drawer.Screen name="Info" component={InfoScreen} />
</Drawer.Navigator>
</NavigationContainer>
</Provider>
);
}
I ussualy use function components to navigate through but with class components it's a little tricky for me. Perhaps I missed something?
So far I 've tried :
this.props.navigation.navigate('Info', {
item: JSON.stringify(data) , });
And it didn't work.
I will be grateful for your help.
Try to use item directly from props, not from state
in your componentDidMount call where you supply from state the scannedKey, supply it from props
firebase.database().ref(`product/${this.props.route.params.item}`)....
you are also calling this.props instead of props directly in your state inside your constructor, which have direct access to it, that's why you can call super(props) and not super(this.props), I am not sure if this is the issue, but in react docs says don't copy props to state because they get ignored, and it's bad practice my friend.
check this link, in the big yellow note what I am reffering to
https://reactjs.org/docs/react-component.html#constructor

I keeping getting this error "cancel all subscriptions and asynchronous tasks in the componentWillUnmount"

I am creating a mobile app using react native and website that utilizes the same firebase. For now I am trying to retrieve data from the firebase and I keep getting this error "Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method." I am not sure if I am suppose of have a closing statement for the component.
import { View,Text,SafeAreaView,FlatList} from "react-native";
import React, { Component } from 'react';
import {stringText} from './ReadNFCScreen';
import {styles} from "../stylesheets/welcomeScreenStyle"
import { List, ListItem} from "react-native-elements";
import * as firebase from 'firebase'
import {config} from '../config/FirebaseAuth'
if (!firebase.apps.length) {
var defaultApp = firebase.initializeApp(config);
console.log(defaultApp.name); // "[DEFAULT]"
}
let paredString = [];
export default class ProductList extends React.Component {
constructor(props) {
super(props);
console.log('checkoutScreen', stringText);
let letters = stringText.toString();
let array = letters.split(',');
console.log('splice', array);
let arr3 = [];
let arr4 = [];
for( let i = 0; i <= array.length; i++)
{
if(array[i] != "Item"){
arr3.push(array[i]);
console.log("did it work:", array[i] );
}
else{
console.log("Nop",array[i] );
}
}
this.state = {
paredString:stringText,
array : array
};
}
componentDidMount(){
const myitems = firebase.database().ref("/Item")
myitems.on("value", datasnap => {
console.log('database Worked!!!', datasnap.val())
})
}
render() {
let {paredString,array } = this.state;
return (
<SafeAreaView>
{/* <ScrollView> */}
<View style={styles.cardContainer}>
<View style={styles.Text}>
{/* <Text style={styles.Textoutput}>{this.state.paredString}</Text> */}
{/* <Text style={styles.Textoutput}>{this.state.array}</Text> */}
<FlatList
data={[{key:array}]}
renderItem={({item}) => (
<Text style={styles.item}>{item.key} </Text>
)}
/>
</View>
</View>
{/* </ScrollView> */}
</SafeAreaView>
);
}
}
When your component ProductList mounts, you attach a listener. As the warning says, you should detach this listener when your component unmounts (in order to avoid a memory leak).
To do it, you can:
store the database reference in your component's state
in componentWillUnmount, detach the listener to the reference
Here is how it looks like:
export default class ProductList extends React.Component {
constructor(props) {
this.state = {
...
myItemsReference: null
};
}
componentDidMount(){
const myitems = firebase.database().ref("/Item");
myitems.on("value", datasnap => { ... });
this.setState({myItemsReference: myitems});
}
componentWillUnmount(){
if(this.state.myItemsReference) {
myItemsReference.off("value");
}
}
render() { ... }
}

Choose firestore subcollection when connecting component to redux with react-redux-firebase

I am using react-redux-firebase's fireStoreConnect() middleware with
a screen in my react-native mobile app. At the time of connecting the component to the redux store, I want to specify the firestore sub-collection I connect to, which depends on the user that is navigating the app.
How should I specify the collection in firestoreConnect? The user id is in the redux store.
MWE:
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { compose } from 'redux';
import { connect } from 'react-redux'
import { firestoreConnect } from 'react-redux-firebase';
class PhotosScreen extends Component {
render() {
return (
<View>
<Text> i plan the use this.props.images here </Text>
</View>
);
}
}
const mapStateToProps = (state) => {
// reference the subcollection of the user
const images = state.firestore.data.images;
return {
images: images,
}
}
export default compose(
firestoreConnect([
{
collection: 'users',
doc: "HOW DO I GET THE USERS ID HERE? IT IS IN REDUX STORE",
subcollections: [{ collection: 'images' }]
}
]),
connect(mapStateToProps),
)(PhotosScreen)
Firestore (and all NoSQL databases) follow an alternating "(parent) collection / document / collection / document ..." hierarchical pattern. To synchronize a React component to subcollections and documents below the parent firestore collection, you need to pass the subcollection/subdocument hierarchy information as props to firestoreConnect.
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { compose } from 'redux';
import { connect } from 'react-redux'
import { firestoreConnect } from 'react-redux-firebase';
class PhotosScreen extends Component {
render() {
return (
<View>
<Text> i plan the use this.props.images here </Text>
{images && images.length ? <div> render your images here using this.props.images and images.map </div> : <p>No images</p>}
</View>
);
}
}
const mapStateToProps = (state) => {
return {
images : state.firestore.data.images, // reference the subcollection of the user
userId : state.firestore.auth.uid // assuming the 'doc id' is the same as the user's uid
}
}
export default compose(
firestoreConnect((props) =>
if (!props.userId) return [] // sync only if the userId is available (in this case, if they are authenticated)
return [
{
collection : 'users', // parent collection
doc : props.userId, // sub-document
subcollections : [
{collection : 'images'} // sub-collection
],
storeAs : 'images'
}
]
}),
connect(mapStateToProps),
)(PhotosScreen)
In 1.x
const enhance = compose(
connect(
(state) => ({
someKey: state.someData
})
),
firebaseConnect(
(props, firebaseInstance) => [
{ path: `${props.someKey}/someData` }
]
)
)
In 2.x
firebaseConnect(
(props, store) => [
{ path: `${store.getState().someKey}/someData` }
]
)
Note how the 2nd argument in firebaseConnect changes from firebaseInstance to store from v1 to v2.
This should get you what you need.

TypeError: Cannot read property 'uid' of null

I am trying to log in with a phone number in my app with firebase but I am facing issue with the login process. I'm not able to login with a phone number in firebase but if I register with a phone number and redirect to the homepage it's working properly. I am using the same method to login, but I got the issue like TypeError: Cannot read property 'uid' of null but I an successfully getting all the console values. I don't know what is being the issue here. But that error is displaying in 3 times repeatedly,
Here is my code:
renderLoginButton() {
if (this.props.loading) {
return (
<Spinner size="large" />
);
}
return (
<Button
style={{ alignSelf: 'flex-start' }}
onPress={this.onLoginBtnClicked.bind(this)}
>
Login
</Button>
);
}
onLoginBtnClicked() {
const { contact, password } = this.props;
const error = Validator('password', password) || Validator('contact', contact);
if (error !== null) {
Alert.alert(error);
} else {
console.log('else');
// this.props.loginUser({ contact, password});
const mobileNo = '+91'+contact;
firebase.auth().signInWithPhoneNumber(mobileNo)
.then(confirmResult =>
console.log(confirmResult),
curr = firebase.auth(),
console.log("curr"+JSON.stringify(curr)),
this.setState({ data: curr}),
NavigationService.navigate('Home')
)
.catch(error => console(error.message) );
}
}
CustomDrawerComponent.js
import React, { Component } from 'react';
import { View, Image, Text } from 'react-native';
import { DrawerItems } from 'react-navigation';
import { connect } from 'react-redux';
import { fetchUserDetails } from '../actions';
class CustomDrawerContentComponent extends Component {
state = {
uri: '',
isfailed: ''
}
componentWillMount() {
this.props.fetchUserDetails();
}
componentWillReceiveProps(nextProps) {
let uri = '';
if (nextProps.ProfilePic !== '') {
uri = nextProps.ProfilePic;
this.setState({ uri, isfailed: false });
} else {
uri = '../images/ic_person_24px.png';
this.setState({ uri, isfailed: true });
}
this.setState({ uri });
}
renderProfileImage() {
if (!this.state.isfailed) {
return (
<Image
style={styles.profileImageStyle}
source={{ uri: (this.state.uri) }}
/>
);
}
return (
<Image
style={styles.profileImageStyle}
source={require('../images/ic_person_24px.png')}
/>
);
}
render() {
console.log('Profile Pic :: ', this.props.ProfilePic);
return (
<View style={styles.container}>
{this.renderProfileImage()}
<Text style={styles.textStyle}>
{this.props.name} - {this.props.category}
</Text>
<DrawerItems {...this.props} />
</View>
);
}
}
const styles = {
container: {
flex: 1,
paddingLeft: 10
},
textStyle: {
fontSize: 14,
textAlign: 'left',
color: '#000000'
},
profileImageStyle: {
alignSelf: 'flex-start',
marginTop: 16,
padding: 10,
width: 40,
height: 40,
borderRadius: 75
}
};
const mapStateToProps = state => {
const { userprofile } = state;
return userprofile;
};
export default connect(mapStateToProps, { fetchUserDetails })(CustomDrawerContentComponent);
callStack:
Why does the user return as undefined (or even null)?
You know there’s a logged in user, you just logged in, heck, you can even see the user object in chrome dev tools.
Then why is it still returning undefined? There’s a straight answer to it.
You’re fetching the user object BEFORE that object is ready to be used.
Now, this can happen because of several different reasons, but if you follow this 2 "rules" you won’t see that error again.
Rule #1: Move it out of the constructor()
When you have something like:
constructor(){
this.userId = firebase.auth().currentUser.uid
}
Over half of the time that page loads, the constructor is going to try to get the user before the user is ready, the app is blocking it because the page isn’t fully loaded, so you’re going to be trying to access uid of a property that just isn’t there yet.
When you get your page fully loaded, you can now call to get the currentUser.uid
Rule #2: Make it an Observable
There’s another approach you can take, that previous Firebase call we just made: firebase.auth().currentUser is synchronous. We can make it asynchronous by subscribing to the auth observable instead.
/**
* When the App component mounts, we listen for any authentication
* state changes in Firebase.
* Once subscribed, the 'user' parameter will either be null
* (logged out) or an Object (logged in)
*/
componentDidMount() {
this.authSubscription = firebase.auth().onAuthStateChanged((user) => {
this.setState({
loading: false,
user,
});
});
}
/**
* Don't forget to stop listening for authentication state changes
* when the component unmounts.
*/
componentWillUnmount() {
this.authSubscription();
}
render() {
// The application is initialising
if (this.state.loading) return null;
// The user is an Object, so they're logged in
if (this.state.user) return <LoggedIn />;
// The user is null, so they're logged out
return <LoggedOut />;
}
}
Source article: Why does Firebase return undefined when fetching the uid?
A good tutorial for React Native will be here: Getting started with Firebase Authentication on React Native
Since, your code did not show much, I hope you make an update to your question to show more code, so I might be able to look through.

Resources