firebase realtime database doesn't work as expected - javascript

I'm a junior dev, so please be easy with me.
My goal is to auth an user anonymously when he open the homepage, then if all went ok, he fetch the data from the real time database, but only if the uid match is ok!
He there are my rules:
{
"rules": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "false",
}
}
}
My database is structured like this https://ibb.co/jkRBCsF
The anonymous sign in is on the context
export const AuthContextProvider = ({ children }: Props) => {
React.useEffect(() => {
signInAnonymously(auth)
.then(() => {
// Signed in..
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
// ...
});
}, []);
const values = {};
return <AuthContext.Provider value={values}>{children}</AuthContext.Provider>;
};
Then when I open a page for example '/crew' getStaticProps launches fetchPlanetInfo
export const getStaticProps: GetStaticProps = async () => {
const planetInfo = await fetchPlanetsInfo("destinations");
return {
props: {
data: planetInfo,
},
};
};
export function fetchPlanetsInfo(query: string) {
let dataArr: any = [];
try {
onAuthStateChanged(auth, (user) => {
if (user) {
const uid = user.uid;
const databaseCall = ref(db, uid + "/" + query);
onValue(databaseCall, (snapshot) => {
const data = snapshot.val();
dataArr.push(data);
});
} else {
console.log("user is signout");
// User is signed out
// ...
}
});
} catch (error) {
console.log(error);
}
return dataArr.flat();
}
I tried to figure out where is the error but I didn't find out the solution
My problem is that the data is not get from the database. fetchPlanetsInfo return null. I think my rules is written wrong.

Related

How to await firebase to start, avoiding Error: "Client is offline" on React Native?

I need help retrieving firebase data in React Native, using expo.
This code works fine when I refresh the app, but when it first starts, it throws an error:
Error: Error: Client is offline.
Maybe I need to do it async and await, I have tried some ways, but no success.
componentDidMount = async () => {
var radioFireData = null;
const { names } = this.props;
const dbRef = ref(db, "records/");
get(child(dbRef, "flap/"))
.then((snapshot) => {
if (snapshot.exists()) {
radioFireData = snapshot.val();
this.setState({ checked: radioFireData[names] });
} else {
console.log("No data available");
}
})
.catch((error) => {
console.log(error);
});
};
Here it is... Maybe I can help someone.
componentDidMount(){
const { names } = this.props;
const reference = ref(db, "records/" + "/flap/");
onValue(
reference,
(snapshot) => {
const data = snapshot.val();
this.setState({ checked: data[names] });
},
{
onlyOnce: true,
}
);
};

how to convert this into a async function?

I want to be able to retrieve the users from the Firestore database and filter to find a match between the id of the current logged in user with the id of the user from the database. I am not able to do that because I can't figure out a way to change this to async function:
const [loggedUser, setLoggedUser] = useState([]);
const [data, setData] = useState([]);
useEffect(() => {
const getUserData = () => {
onSnapshot(collection(db, "users"), (snapshot) => {
let list = [];
snapshot.docs.forEach((doc) => {
list.push({ id: doc.id, ...doc.data() });
setData(list);
});
}, (err) => {
console.log(err);
});
}
getUserData();
}, [])
useEffect(() => {
const getLoggedUser = onAuthStateChanged(auth, (user) => {
if (user) {
const uid = user.uid;
console.log(uid);
if (data) {
const signedUser = data.filter((item) => item.id === uid);
setLoggedUser(signedUser);
} else {
console.log("no matching data")
}
} else {
console.log("no user found")
}
});
getLoggedUser();
}, [])
I want to be able to retrieve the users from the Firestore database and filter to find a match between the id of the current logged in user with the id of the user from the database.
You can use getDoc instead that'll only fetch the user's document and will cost you only 1 read. Currently you are reading the whole collection that'll cost you N reads where N is number of documents in the users collection.
You can use useEffect() just once and query Firestore when the auth state has been updated. Try refactoring the code as shown below:
import { getDoc, doc } from "firebase/firestore"
const [loggedUser, setLoggedUser] = useState([]);
const [data, setData] = useState([]);
useEffect(() => {
onAuthStateChanged(auth, (user) => {
if (user) {
const uid = user.uid;
console.log("User UID:", uid);
const snapshot = await getDoc(doc(db, "users", uid));
if (snapshot.exists) {
setLoggedUser(snapshot.data());
} else {
console.log("user document missing")
}
} else {
console.log("User not logged in")
}
});
}, [])

how to get firebase use in nextJS getserversideprops

I want to get 'sample' document in Firestore using getServerSideProps if there is signed user.
Code below doesn't work. It's result is 'can't read'
What should I do? or is there the other way?
export const getServerSideProps = () => {
let currentUser = []
authService.onAuthStateChanged(async user => {
if(user) {
const docRef = dbService.collection('whole_users').doc('sample').get()
await docRef.then((doc) => {
if(doc.exists) {
currentUser.push(doc.data())
}
})
} else {
console.log("can't read")
}
})
return {
props: {currentUser}
}
}
The first:
You call get() without an await. Chaneg your code to this:
export const getServerSideProps = () => {
let currentUser = []
authService.onAuthStateChanged(async user => {
if(user) {
const docRef = dbService.collection('whole_users').doc('sample')
await docRef.get().then((doc) => {
if(doc.exists) {
currentUser.push(doc.data())
}
})
} else {
console.log("can't read")
}
})
return {
props: {currentUser}
}
}
The second: onAuthStateChanged is only for the client side. To access the auth state on the server side you would need to put the auth state into a provider. Here is an example how to do it.

Async issue with Link

I'm running into an async issue I hope I can get help in. The error I'm getting is
index.js:2178 Warning: Can't call setState (or forceUpdate) 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.
in CreateRoomPage (created by Route)
The location of this error is within this function. The two location are found where it says loginPromise.then and the setState which is at the bottom of the function. I'm currently calling this function when a person clicks on .
createUser(e) {
if (this.state.username === "") {
e.preventDefault();
this.setState({
errors: "Username can't be blank"
});
return;
}
const loginPromise = new Promise((resolve, reject) => {
firebase.auth().onAuthStateChanged(user => {
if (user) {
window.user = user;
resolve(user.uid);
} else {
firebase
.auth()
.signInAnonymously()
.then(user => {
resolve(user.uid);
})
.catch(err => {
console.log(err);
});
}
});
});
loginPromise.then(id => {
let db = firebase.database();
let playersRef = db.ref(`Room/${this.state.roomId}/players`);
playersRef.child(`${id}`).set(`${this.state.username}`);
let player = db.ref(`Room/${this.state.roomId}/players/${id}`);
player.onDisconnect().remove();
let allPlayers = db.ref(`Room/${this.state.roomId}/all-players`);
allPlayers.child(`${id}`).set(true);
let allPlayer = db.ref(`Room/${this.state.roomId}/all-players/${id}`);
allPlayer.onDisconnect().remove();
let scoreBoard = db.ref(`Room/${this.state.roomId}/scoreBoard`);
scoreBoard.child(`${this.state.username}`).set(0);
let playerScore = db.ref(
`Room/${this.state.roomId}/scoreBoard/${this.state.username}`
);
playerScore.onDisconnect().remove();
let creator = db.ref(`Room/${this.state.roomId}`);
creator.child("creator").set(`${id}`);
db.ref(`Room/${this.state.roomId}`)
.child("gameStarted")
.set(false);
this.setState({
username: "",
errors: ""
});
});
I've spend nearly 3 hours trying to figure this out. I'm hoping someone can teach me where I'm making this error. I've tried to use a local state where once the componentDidMount it'll change local state to true and back to false once it unmounted like so:
componentDidMount() {
this.setState({ isMounted: true }, () => {
if (this.state.isMounted) {
let db = firebase.database();
let roomRefKey = db.ref("Room").push().key;
this.setState({
roomId: roomRefKey
});
}
});
}
Below is another place where it throws such an error
createUser(e) {
e.preventDefault();
if (
this.state.username.length === 0 &&
this.state.accesscode.length === 0
) {
this.setState({
errors: {
username: "Username can't be blank",
accesscode: "Access Code can't be blank"
}
});
return;
}
if (this.state.username.length === 0) {
this.setState({
errors: { username: "Username can't be blank", accesscode: "" }
});
return;
}
if (this.state.accesscode.length === 0) {
this.setState({
errors: { username: "", accesscode: "Access Code can't be blank" }
});
return;
}
const loginPromise = new Promise((resolve, reject) => {
firebase.auth().onAuthStateChanged(user => {
if (user) {
window.user = user;
resolve(user.uid);
} else {
firebase
.auth()
.signInAnonymously()
.then(user => {
resolve(user.uid);
})
.catch(err => {
console.log(err);
});
}
});
});
loginPromise.then(id => {
let db = firebase.database();
let playersRef = db.ref(`Room/${this.state.accesscode}/players`);
playersRef.child(`${id}`).set(`${this.state.username}`);
let player = db.ref(`Room/${this.state.accesscode}/players/${id}`);
player.onDisconnect().remove();
let allPlayers = db.ref(`Room/${this.state.accesscode}/all-players`);
allPlayers.child(`${id}`).set(true);
let allPlayer = db.ref(`Room/${this.state.accesscode}/all-players/${id}`);
allPlayer.onDisconnect().remove();
let scoreBoard = db.ref(`Room/${this.state.accesscode}/scoreBoard`);
scoreBoard.child(`${this.state.username}`).set(0);
let playerScore = db.ref(
`Room/${this.state.accesscode}/scoreBoard/${this.state.username}`
);
playerScore.onDisconnect().remove();
this.props.history.push({
pathname: `/waiting-room/${this.state.accesscode}`
});
});
}

"querySnapshot.forEach is not a function" in firestore

I'm trying to do a query to the database, to get all documents of sub-collection "roles" to redirect to different routes.
let userRef1 = db.collection('users').doc(currentUser.uid).collection('roles')
let cont = 0
let rol = ''
let rolStatus = ''
userRef1.get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
cont++
rol = doc.data().rol
rolStatus = doc.data().status
});
import { firestore } from "../../firebase";
export const loadCategories = () => {
return (dispatch, getState) => {
firestore
.collection("CATEGORIES")
.get()
.then((querySnapshot) => {
if (!querySnapshot.empty) {
querySnapshot.forEach((doc) => {
console.log(doc.id, "=>", doc.data());
});
}
})
.catch((error) => {
console.log(error);
});
};
};
I have a collection of users including uid just like yours. And for each user, it contains a sub-collection called friends.
Currently, I'm using the following code for my project without having any issues.
module.exports = ({ functions, firestore }) => {
return functions.firestore.document('/users/{uid}').onDelete((event) => {
const userFriendsRef = getFriendsRef(firestore, uid);
userFriendsRef.get().then(snapshot => {
if (snapshot.docs.length === 0) {
console.log(`User has no friend list.`);
return;
} else {
snapshot.forEach(doc => {
// call some func using doc.id
});
}
}
}
};
function getFriendsRef(firestore, uid) {
return firestore.doc(`users/${uid}`).collection('friends');
}
Give it a try to fix your code from
db.collection('users').doc(currentUser.uid).collection('roles')
to
db.doc(`users/${currentUser.uid}`).collection('roles')
It is not clear what you are doing with the rol and status variables. You have declared them as though you are storing a single value, yet you are returning an array of roles and iterating through them.
With regards to getting the results, if your browser supports ES6, then you could do the following:
let userRef1 = db.collection(`users/${currentUser.uid}/roles`)
let cont = 0
let rol;
let rolStatus;
return userRef1.get()
.then(querySnapshot => {
// Check if there is data
if(!querySnapshot.empty) {
// Create an array containing only the document data
querySnapshot = querySnapshot.map(documentSnapshot => documentSnapshot.data());
querySnapshot.forEach(doc => {
let {rol, status} = doc;
console.log(`rol: ${rol} - status: ${status}`);
});
} else {
console.log('No data to show');
}
})
.catch(err => {
console.error(err);
});
Please note: I've only tested this with the Node SDK
// Firebase App (the core Firebase SDK) is always required and must be listed first
import * as firebase from "firebase/app";
// Add the Firebase products that you want to use
import "firebase/auth";
import "firebase/firestore";
const firebaseConfig = {
apiKey: "AIzaSyDNdWutrJ3Axpm-8ngNhzkzcw1g3QvkeFM",
authDomain: "books-7bd8b.firebaseapp.com",
databaseURL: "https://books-7bd8b.firebaseio.com",
projectId: "books-7bd8b",
storageBucket: "books-7bd8b.appspot.com",
messagingSenderId: "385706228283",
appId: "1:385706228283:web:a3c2c9585dd74d54693a1e",
};
firebase.initializeApp(firebaseConfig);
export const firebaseAuth = firebase.auth();
export const firestore = firebase.firestore();
export default firebase;
You should check if it always exists before doing your logic:
userRef1.get().then(function(querySnapshot) {
if(querySnapshot)
{
querySnapshot.forEach(function(doc) {
...you thing
}
})

Categories