how to convert this into a async function? - javascript

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")
}
});
}, [])

Related

Add a new collection in firebase when the user create account using uid of user in react js

I make to to do list website but I want to create for each new user who register a new collection. when it created must also create automatically a new doucment in firebase for it and then write the data(to do list) in this docu. (I am using firebase cloud)
here a new structure which i wish:
const handleSignUp = (e) => { // singup function in Component Signup.jsx
e.preventDefault();
createUserWithEmailAndPassword(auth, email, password)
.then(() => {
// Signed up
navigate("/");
})
.catch((error) => {
alert.message("error");
// ..
}); };
// create todo in Main.jsx
const createTodo = async (e) => {
e.preventDefault(e); // no reload for the page
if (input === "") {
alert("pleas enter a valid statment");
return;
} else {
const docRef = await addDoc(collection(db, "todo"), {
text: input,
completed: false,
});
console.log("Document written with ID: ", docRef.id);
}
setInput("");
};
The structure you needs for this is:
Users (collection)
$uid (documents)
Todos (collection)
$todoId (documents)
After the user is signed in, you can then add a Todo to their collection with:
const uid = auth.currentUser.uid;
const todos = collection(db, "Users", uid, "Todos");
const docRef = await addDoc(todos, {
text: input,
completed: false,
});

How to get an element inside of an array in a function?

Code below outputs an array of users stored in Firestore db. Each document have the same id of a user.
const [user] = useAuthState(auth);
const [userData, setUserData] = useState([]);
const usersDB = collection(firestore, "Users");
const getUsers = async () => {
const data = await getDocs(usersDB);
setUserData(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
useEffect(() => {
getUsers();
}, []);
I want to access the document with same id as logged in user and check if isAdmin field is true or false using useAuthState to get user uid. How should I write the code for this?
According to your code you are using getDocs instead of getDoc.
Here's a sample code using useEffect and checking boolean isAdmin.
const [userData, setUserData] = useState([]);
const email = "test#xyz.com";
const password = "123Addw1113#";
const getUsers = async () => {
signInWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
const user = userCredential.user;
setDoc(doc(db, "<collection>", user.uid), {
someField: "<data>",
})
.then(async () => {
const docRef = doc(db, "<collection>", user.uid);
const docSnap = await getDoc(docRef);
if (docSnap.exists() && docSnap.data().isAdmin === true) {
setUserData(docSnap.data());
console.log("isAdmin is True");
} else if (docSnap.exists() && docSnap.data().isAdmin === false) {
console.log("isAdmin is False");
} else {
console.log("No such document!");
}
});
})
.catch((error) => {
console.log(error)
});
}
useEffect(() => {
getUsers();
}, []);

React Native Firestore: How do I listen for database changes at the same time as using the .where() query?

I have made a FlatList that gets populated from a firestore database. I can currently do all the CRUD operations, but when I edit an entry, it doesn't change in the FlatList. It does change in the firestore database.
I suspect it's because I'm not using .onSnapshot(). My problem is that I need to filter the data using .where() and I haven't been able to find out how to combine the two operations.
My code looks like this:
export const Coach = () => {
const navigation = useNavigation();
const [user, setUser] = useState();
const [userName, setUserName] = useState('');
const [workoutIds, setWorkoutIds] = useState([]);
const [workouts, setWorkouts] = useState([]);
const userRef = firestore().collection('Users');
const workoutRef = firestore().collection('Workouts');
// Setting the user state
auth().onAuthStateChanged(userInstance => {
if (userInstance) {
setUser(userInstance);
}
});
// Getting coach id's from firestore - Started out at individual workout id's
useEffect(() => {
if (user) {
const subscriber = userRef.doc(user.uid).onSnapshot(userSnap => {
if (userSnap) {
setUserName(userSnap.data().Name);
setWorkoutIds(userSnap.data().Workouts);
}
});
return () => subscriber();
}
}, [user]);
// using the list of coach id's to get workouts
useEffect(() => {
if (workoutIds.length != 0) {
let workoutList = [];
workoutRef
.where(firestore.FieldPath.documentId(), 'in', workoutIds)
.get()
.then(query => {
query.forEach(snap => {
workoutList.push({...snap.data(), key: snap.id});
});
setWorkouts(workoutList);
});
}
}, [workoutIds]);
The problem should lie in the last useEffect block.
So how do I get it to listen for changes and update the FlatList, while still using the .where()?
----------------------------------------- Edit -----------------------------------------
I have tried to add an onSnapshot to my query:
Before:
// using the list of coach id's to get workouts
useEffect(() => {
if (workoutIds.length != 0) {
let workoutList = [];
workoutRef
.where(firestore.FieldPath.documentId(), 'in', workoutIds)
.get()
.then(query => {
query.forEach(snap => {
workoutList.push({...snap.data(), key: snap.id});
});
setWorkouts(workoutList);
});
}
}, [workoutIds]);
After:
// using the list of coach id's to get workouts
useEffect(() => {
if (workoutIds.length != 0) {
let workoutList = [];
workoutRef
.where(firestore.FieldPath.documentId(), 'in', workoutIds)
.onSnapshot(query => {
query.forEach(snap => {
workoutList.push({...snap.data(), key: snap.id});
});
setWorkouts(workoutList);
});
}
}, [workoutIds]);
It still doesn't update the view straight away and now I get an error about encountering two of the same keys.
To solve the issue I had to add .onSnapshot() to my query for it to listen to changes in the database. On top of that I accidentally put the temporary list that I added objects to, outside the onSnapshot(), so it just kept adding on. After moving the temporary list into the onSnapshot(), it now updates.
Before:
useEffect(() => {
if (workoutIds.length != 0) {
let workoutList = [];
workoutRef
.where(firestore.FieldPath.documentId(), 'in', workoutIds)
.get()
.then(query => {
query.forEach(snap => {
workoutList.push({...snap.data(), key: snap.id});
});
setWorkouts(workoutList);
});
}
}, [workoutIds]);
After:
useEffect(() => {
if (workoutIds.length != 0) {
workoutRef
.where(firestore.FieldPath.documentId(), 'in', workoutIds)
.onSnapshot(query => {
let workoutList = [];
query.forEach(snap => {
workoutList.push({...snap.data(), key: snap.id});
});
setWorkouts(workoutList);
});
}
}, [workoutIds]);

firebase realtime database doesn't work as expected

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.

Firestore database won't appear

I'm new with Firebase and I'm still learning how it works. I've created a login, signup and logout sections. The users appear registrated and are saved in the authentication section, but I also want to have their data in database when they register for the first time. I thought of using Firestore Database. The problem is that everything seems to work, but nothing appears in my database section. At first I thought that I was not passing any user auth to the function, so i created a condition to test if there's no user auth, then show a warning. However, there's no warning so it means that I passed it properly.
This is how the sign up function works:
export const SignUp = () => {
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const displayName = useRef();
const email = useRef();
const password = useRef();
const passwordConfirmRef = useRef();
const handleSubmit = async e => {
e.preventDefault();
if (password.current.value !== passwordConfirmRef.current.value) {
return setError("Passwords do not match");
}
try {
setError("")
setLoading(true)
const { user } = await auth.createUserWithEmailAndPassword(email.current.value, password.current.value)
const userRef = await handleUserProfile(user, displayName.current.value)
console.log(userRef)
} catch {
setError("Failed to create an account")
}
setLoading(false)
}
I create the user with auth.createUserWithEmailAndPassword and then I pass the user to handleUserProfile
Here's handleUserProfile function:
export const handleUserProfile = async (userAuth, additionalData) => {
if (!userAuth) {
console.warn("No userAuth")
return
}
const { displayName, email } = userAuth;
const timestamp = new Date()
try {
return await firestore.collection("users").add({
displayName,
email,
timestamp,
...additionalData
})
} catch (err) {
console.log(err)
}
return null;
};
Then, nothing appears in my database and nothing gets added. I'm not sure what I'm doing wrong.
The firestore.collections().add function is adding objects (key: value pairs).
It looks like you're not passing an object into handleUserProfile:
const userRef = await handleUserProfile(user, displayName.current.value)
Passing an object into additionalData should solve your issue.

Categories