componentDidMount() {
this.authListener = auth.onAuthStateChanged( async userAuth => {
if(userAuth){
const userRef = await handleUserProfile(userAuth); userRef.onSnapshot(snapshot=>{
this.setState({ currentUser:{ id:snapshot.id, ...snapshot.data() } }) }) } this.setState({ ...initialState })
});
}
Your function handleUserProfile is not returning the userRef so you get that error. It would be great to provide more that to help you more with this. Maybe the code of that function handleUserProfile.
Can you try to updated your handleUserProfile function to:
export const handleUserProfile = async ({ userAuth, additionalData = {} }) => {
if (!userAuth) {
console.warn("No userAuth provided!");
return;
}
const { uid } = userAuth;
const userRef = firestore.doc(`users/${uid}`);
const snapshot = await userRef.get();
if (!snapshot.exists) {
const { displayName, email } = userAuth;
const timestamp = new Date();
try {
await userRef.set({
displayName,
email,
createdDate: timestamp,
...additionalData,
});
} catch (err) {
console.warn(err);
}
}
return userRef;
};
If you are getting the log No userAuth provided! you are not sending a userAuth to it.
Related
I am creating a blog application. In every parameterized route I get the details of that specific user.For eg: /profile/#randomuser get the details of #randomusers, /profile/#robert get the details of #robert. I get the details of parameterized toute users but not the user who log in to the blog application.
Everything is imported correctly.
<Route path="/profile/:profile" element={\<Profile /\>}
Profile.jsx
const [user, getUser] = useState([])
const [glbUser, setUser] = useContext(UserContext)
const match = useParams().profile
const match = useMatch('/profile/:profile')
const userMatch = match.params.profile
console.log(" usematchis ", userMatch)
const userParams = useParams().profile
useEffect(() => {
async function fetchGlbUser() {
const loggedInUser = window.localStorage.getItem('userToken')
if (loggedInUser) {
const user = JSON.parse(loggedInUser)
loginServices.setToken(user.token)
// loginServices.createToken()
const config = { headers: { Authorization: user.token } }
const glbUser = await axios.get("${postUrl}", config)
setUser(glbUser.data.glbUserToken)
return glbUser.data.glbUserToken
}
}
fetchGlbUser()
}, [])
// console.log("Match is", match)
// console.log("Type of Match is", typeof match.params.profile)
useEffect(() =\> {
axios.get("http://localhost:9000/api/users/${userMatch}", { params: { profile: userMatch } })
.then(res =\> {
console.log(res.data)
getUser(res.data)
// getUser(res.data.newUser)
})
.catch(err => console.log(err))
// const getUserData = async () => {
// const res = loginServices.getProfile(`http://localhost:9000/api/users/${userMatch}`, {params:{profile:userMatch}})
// return res.data
// }
// getUserData()
}, [])
Backend.js
router.get('/:profile', async (req, res) =\> {
console.log(req)
const username = req.params.profile
const decodedToken = jwt.verify(getToken(req), process.env.SECRET_KEY)
// console.log(username)
// console.log(typeof username)
try {
const newUser = await User.findOne({ username })
const glbUserToken = await User.findById(decodedToken.id)
// console.log(newUser)
res.status(200).json({ newUser, glbUserToken })
} catch (err) {
console.log(err)
}
})
const getToken = req =>
const auth = req.get('authorization')
if (auth && auth.startsWith(`bearer `))
return auth.replace('bearer ', '') }
return null
}
I'm trying to implement redux saga in my old project and i get strange error.
I'm getting back the userData with all user details but my middleware logger tells me that sign_in_failed fires instead of sign_in_success with this error:
Cannot read property of undefined data() Firebase.
So i tried to do optional chaining and i also tried the old way of deep property check (with &&) but this didnt help. Also, i cant anymore perform a stripe payments due to this error because stripe cant see the user.
UserSaga.jsx
import { takeLatest, all, call, put } from 'redux-saga/effects'
import { USER_ACTIONS_TYPES } from '../../actions/user/userActionsTypes'
import {
signInSuccess,
signInFailed,
signUpSuccess,
signUpFailed,
signOutSuccess,
signOutFailed
} from '../../actions/user/userActionCreator'
import {
getCurrentUser,
createUserDocumentFromAuth,
createAuthUserWithEmailAndPassword,
signInWithGooglePopup,
signInAuthUserWithEmailAndPassword,
signOutAuthUser
} from '../../../database/firebase.config'
export function* getUserSnapshot(userAuth, additionalInformation) {
try {
const userSnapshot = yield call(
createUserDocumentFromAuth,
userAuth,
additionalInformation
);
//yield put(signInSuccess({ id: userSnapshot?.id, ...userSnapshot.data()}));
// console.log('userSnapshot2>>>>>>>>>', ...userSnapshot.data())
const id = userSnapshot?.id;
const userData = userSnapshot.data()
const {createdAt, email, password}= userData
yield put(signInSuccess({ id, createdAt, email, password }));
console.log('userSnapshot2>>>>>>>>>', userSnapshot)
} catch (error) {
yield put(signInFailed(error.message))
}
}
export function* signInWithGoogle() {
try {
const { user } = yield call(signInWithGooglePopup)
yield call(getUserSnapshot, user)
} catch (error) {
yield put(signInFailed(error))
}
}
export function* signInWIthEmail({ payload: { email, password } }) {
try {
const { user } = yield call(signInAuthUserWithEmailAndPassword, email, password)
yield call(getUserSnapshot, user)
} catch (error) {
yield put(signInFailed(error))
}
}
export function* isUserAuth() {
try {
const userAuth = yield call(getCurrentUser)
if (!userAuth) return;
yield call(getUserSnapshot, userAuth)
} catch (error) {
yield put(signInFailed(error))
}
}
export function* signUpWIthEmail({ payload: { email, password, displayName } }) {
try {
const { user } = yield call(createAuthUserWithEmailAndPassword, email, password)
yield put(signUpSuccess(user, { displayName }))
} catch (error) {
yield put(signUpFailed(error))
}
}
export function* signOut() {
try {
yield call(signOutAuthUser)
yield put(signOutSuccess())
} catch (error) {
yield put(signOutFailed(error))
}
}
export function* signInAfterSignUp({ payload: { user, additionalDetails } }) {
yield call(getUserSnapshot, user, additionalDetails)
}
export function* onGoogleSignInStart() {
yield takeLatest(USER_ACTIONS_TYPES.GOOGLE_SIGN_IN_START, signInWithGoogle)
}
export function* onCheckUserSession() {
yield takeLatest(USER_ACTIONS_TYPES.CHECK_USER_SESSION, isUserAuth)
}
export function* onEmailSignInStart() {
yield takeLatest(USER_ACTIONS_TYPES.EMAIL_SIGN_IN_START, signInWIthEmail)
}
export function* onSignUpStart() {
yield takeLatest(USER_ACTIONS_TYPES.SIGN_UP_START, signUpWIthEmail)
}
export function* onSignUpSuccess() {
yield takeLatest(USER_ACTIONS_TYPES.SIGN_IN_SUCCESS, signInAfterSignUp)
}
export function* onSignOutStart() {
yield takeLatest(USER_ACTIONS_TYPES.SIGN_OUT_START, signOut)
}
export function* userSagas() {
yield all([
call(onCheckUserSession),
call(onGoogleSignInStart),
call(onEmailSignInStart),
call(onSignUpStart),
call(onSignUpSuccess),
call(onSignOutStart),
call(onSignUpSuccess)
])
}
firebase.config.js
// Initialize Firebase and Oauth
initializeApp(firebaseConfig);
const googleProvider = new GoogleAuthProvider();
googleProvider.setCustomParameters({
prompt: "select_account"
})
//Initialize GAuth with popup signin method
export const auth = getAuth()
export const signInWithGooglePopup = () => signInWithPopup(auth, googleProvider)
// export const signInWithGoogleRedirect = () => signInWithRedirect(auth, googleProvider)
// Initialize Firestore
export const db = getFirestore()
// get the conllection created with all docs
export const getCollectionWithDocuments = async() => {
const collectionRef = collection(db, 'categories')
const q = query(collectionRef)
const querySnapShot = await getDocs(q)
return querySnapShot.docs.map(docSnapshot => docSnapshot.data())
}
//Create a user document
export const createUserDocumentFromAuth = async(userAuth, additionalInformation = {}) => {
if (!userAuth) return;
const userDocRef = doc(db, 'users', userAuth.uid)
// console.log('userRef>>', userDocRef)
const userSnapshot = await getDoc(userDocRef)
// console.log('just snapshot>>>', snapShot)
// console.log('if shot exist>>', snapShot.exists())
if (!userSnapshot.exists()) {
const { displayName, email } = userAuth
const createdAt = new Date();
try {
await setDoc(userDocRef, {
displayName,
email,
createdAt,
...additionalInformation
})
} catch (error) {
console.log("error creating user", error.message)
}
}
return userSnapshot;
}
export const createAuthUserWithEmailAndPassword = async(email, password) => {
if (!email || !password) return;
return await createUserWithEmailAndPassword(auth, email, password)
}
export const signInAuthUserWithEmailAndPassword = async(email, password) => {
if (!email || !password) return;
return await signInWithEmailAndPassword(auth, email, password)
}
export const signOutAuthUser = async() => await signOut(auth)
export const onAuthObserver = (callback) => onAuthStateChanged(auth, callback)
//Create a collection of document in firestore (call it ones inside useeffect to avoid mutations)
export const createCollectionWithDocuments = async(collectionName, objectsToAdd) => {
const collectionRef = collection(db, collectionName)
const batch = writeBatch(db)
objectsToAdd.forEach(object => {
const docRef = doc(collectionRef, object.title.toLowerCase())
batch.set(docRef, object)
})
await batch.commit()
console.log('collection created')
}
export const getCurrentUser = () => {
return new Promise((resolve, reject) => {
const unsubscribe = onAuthStateChanged(
auth,
(userAuth) => {
unsubscribe();
resolve(userAuth);
// console.log(userAuth)
},
reject)
})
}
userReducer.jsx
export const INITIAL_STATE = {
currentUser: null,
isLoading: false,
error: null
}
export const userReducer = (state = INITIAL_STATE, action) => {
const { type, payload } = action;
switch (type) {
case USER_ACTIONS_TYPES.SIGN_IN_SUCCESS:
return {
...state,
currentUser: payload,
error: null
}
case USER_ACTIONS_TYPES.SIGN_OUT_SUCCESS:
return {
...state,
currentUser: null
}
case USER_ACTIONS_TYPES.SIGN_OUT_FAILED:
case USER_ACTIONS_TYPES.SIGN_UP_FAILED:
case USER_ACTIONS_TYPES.SIGN_IN_FAILED:
return {
...state,
error: payload
}
default:
return state
}
}
userActions.js
export const emailSignInStart = (email, password) => createActions(USER_ACTIONS_TYPES.EMAIL_SIGN_IN_START, { email, password })
export const signInSuccess = user => createActions(USER_ACTIONS_TYPES.SIGN_IN_SUCCESS, user)
export const signInFailed = error => createActions(USER_ACTIONS_TYPES.SIGN_IN_FAILED, error)
I'm trying to add a new document to a non-existing sub-collection in a document(solutionsCollection/docID/commentsSubCollection).
But I'm getting this error message when I add a document to the sub-collection:
TypeError: doc is not a function at addSubCollectionDocument
Code to add a new document to a comments sub-collection:
const addSubCollectionDocument = async (docID, doc) => {
dispatch({ type: "IS_PENDING" })
try {
const docRef = doc(db, c, docID)
const colRef = collection(docRef, "comments")
const addedDocument = await addDoc(colRef, doc)
dispatchIfNotCancelled({ type: "ADDED_DOCUMENT", payload: addedDocument })
return addedDocument
} catch (error) {
console.log(error)
dispatchIfNotCancelled({ type: "ERROR", payload: error })
return null
}
}
handleSubmit function:
const handleSubmit = async (e) => {
e.preventDefault()
try {
const commentToAdd = {
id: Math.floor(Math.random() * 10000),
content: newComment.trim(),
reactions: [],
user: {
userID: user.uid,
avatarURL: user.photoURL,
displayName: user.displayName,
username: user.reloadUserInfo.screenName,
},
replies: [],
createdAt: new Date(),
}
await addSubCollectionDocument(id, commentToAdd)
setNewComment("")
if (response) {
console.log(response.error)
}
} catch (error) {
console.log(error)
}
}
useFirestore hook code:
import { useEffect, useReducer, useState } from "react"
import {
addDoc,
collection,
deleteDoc,
doc,
serverTimestamp,
updateDoc,
} from "firebase/firestore"
import { db } from "../firebase/config"
export const useFirestore = (c) => {
const [response, dispatch] = useReducer(firestoreReducer, initialState)
const [isCancelled, setIsCancelled] = useState(false)
// only dispatch is not cancelled
const dispatchIfNotCancelled = (action) => {
if (!isCancelled) {
dispatch(action)
}
}
// add a document
const addDocument = async (doc) => {
dispatch({ type: "IS_PENDING" })
try {
const createdAt = serverTimestamp()
const addedDocument = await addDoc(collection(db, c), {
...doc,
createdAt,
})
dispatchIfNotCancelled({ type: "ADDED_DOCUMENT", payload: addedDocument })
} catch (err) {
dispatchIfNotCancelled({ type: "ERROR", payload: err.message })
}
}
// Add document to sub collection
const addSubCollectionDocument = async (docID, doc) => {
dispatch({ type: "IS_PENDING" })
try {
const docRef = doc(db, c, docID)
const colRef = collection(docRef, "comments")
const addedDocument = await addDoc(colRef, doc)
dispatchIfNotCancelled({ type: "ADDED_DOCUMENT", payload: addedDocument })
return addedDocument
} catch (error) {
console.log(error)
dispatchIfNotCancelled({ type: "ERROR", payload: error })
return null
}
}
useEffect(() => {
return () => setIsCancelled(true)
}, [])
return {
addDocument,
addSubCollectionDocument,
response,
}
}
File where I'm importing addSubCollectionDocument hook:
import { useFirestore } from "../../hooks/useFirestore"
import { useAuthContext } from "../../hooks/useAuthContext"
const SolutionComments = ({ solution }) => {
const [newComment, setNewComment] = useState("")
const { user } = useAuthContext()
const { id } = useParams()
const { addSubCollectionDocument, response } = useFirestore("solutions")
const handleSubmit = async (e) => {
e.preventDefault()
try {
const commentToAdd = {
id: Math.floor(Math.random() * 10000),
content: newComment.trim(),
reactions: [],
user: {
userID: user.uid,
avatarURL: user.photoURL,
displayName: user.displayName,
username: user.reloadUserInfo.screenName,
},
replies: [],
createdAt: new Date(),
}
await addSubCollectionDocument(id, commentToAdd)
setNewComment("")
if (response) {
console.log(response.error)
}
} catch (error) {
console.log(error)
}
}
return (...)}
Package.json gist:
https://gist.github.com/rishipurwar1/57fbf348cd9755a940e7e3f218de570f
Firebase rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /challenges/{challenge}{
allow read: if true
}
match /resources/{resource}{
allow read: if true
}
match /solutions/{solution}{
allow read: if true
allow create: if request.auth.uid != null;
allow update, delete: if request.auth.uid == resource.data.userID;
}
match /users/{userId}{
allow create: if true
allow read: if true
}
}
}
const addSubCollectionDocument = async (docID, doc) => { ... })
// That "doc" parameter is not a function ^^^
That parameter seems to be an object that you are passing to addSubCollectionDocument(). Try renaming that to something else like:
const addSubCollectionDocument = async (docID, docData) => {
const colRef = collection(db, c, docID, "comments")
const addedDocument = await addDoc(colRef, doc)
})
You haven't specified security rules for your comments sub-collection. Try adding the following rules:
match /c/{docId}/comments/{commentId} {
allow read, write: if true;
}
Do update the if true to required rules as per your use case.
i have a firebase issue, whenever I try to authenticate with firebase i get this problem
App.js:27 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'onSnapshot')
App.js :
{
const {setCurrentUser} = this.props
this.authListener = auth.onAuthStateChanged(async userAuth =>{
if(userAuth)
{
const userRef = await handleUserProfile(userAuth)
userRef.onSnapshot(snapchot=>{
setCurrentUser(
{
id:snapchot.id,
...snapchot.data(),
}
)
})
}
setCurrentUser(userAuth)
})
}
firebase.js :
export const handleUserProfile = async({ userAuth, additionalData }) => {
if (!userAuth) return;
const { uid } = userAuth;
const userRef = firestore.doc(`users/${uid}`);
const snapshot = await userRef.get();
if (!snapshot.exists) {
const { displayName, email } = userAuth;
const timestamp = new Date();
const userRoles = ['user'];
try {
await userRef.set({
displayName,
email,
createdDate: timestamp,
userRoles,
...additionalData
});
} catch (err) {
console.log(err);
}
}
return userRef;
};
I cant get eventually the redux state of the currentUser,
I am trying to save the currentUser when is logged in
:
const mapStatetoProps = ({user}) =>
({
currentUser:user.currentUser
})
const mapDispatchToProps = dispatch =>({
setCurrentUser:user => dispatch(setCurrentUser(user))
})
export default connect(mapStatetoProps,mapDispatchToProps)(App)
Here you are destructuring userAuth from the object you receive as a parameter in this function
export const handleUserProfile = async({ userAuth, additionalData }) => {
And here you are directly passing the userAuth:
const userRef = await handleUserProfile(userAuth)
Instead, here you should write:
const userRef = await handleUserProfile({userAuth})
This will pass the userAuth in an object, hence the destructuring will be successful!
I am trying to load a website to my localhost but keep running into an error that says Uncaught ReferenceError: keyword is not defined at useFetch (:3000/src/hooks/useFetch.jsx:21:7) at TransactionsCard (:3000/src/components/Transactions.jsx:33:18). The issue hapens in my code where I'm fetching gifs from my API at giphy developer.
Here is the source code:
Transactions.jsx:
import React, { useEffect, useState } from "react";
import { ethers } from "ethers";
import { contractABI, contractAddress } from "../utils/constants";
export const TransactionContext = React.createContext();
const { ethereum } = window;
const createEthereumContract = () => {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const transactionsContract = new ethers.Contract(contractAddress, contractABI, signer);
return transactionsContract;
};
export const TransactionsProvider = ({ children }) => {
const [formData, setformData] = useState({ addressTo: "", amount: "", keyword: "", message: "" });
const [currentAccount, setCurrentAccount] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [transactionCount, setTransactionCount] = useState(localStorage.getItem("transactionCount"));
const [transactions, setTransactions] = useState([]);
const handleChange = (e, name) => {
setformData((prevState) => ({ ...prevState, [name]: e.target.value }));
};
const getAllTransactions = async () => {
try {
if (ethereum) {
const transactionsContract = createEthereumContract();
const availableTransactions = await transactionsContract.getAllTransactions();
const structuredTransactions = availableTransactions.map((transaction) => ({
addressTo: transaction.receiver,
addressFrom: transaction.sender,
timestamp: new Date(transaction.timestamp.toNumber() * 1000).toLocaleString(),
message: transaction.message,
keyword: transaction.keyword,
amount: parseInt(transaction.amount._hex) / (10 ** 18)
}));
console.log(structuredTransactions);
setTransactions(structuredTransactions);
} else {
console.log("Ethereum is not present");
}
} catch (error) {
console.log(error);
}
};
const checkIfWalletIsConnect = async () => {
try {
if (!ethereum) return alert("Please install MetaMask.");
const accounts = await ethereum.request({ method: "eth_accounts" });
if (accounts.length) {
setCurrentAccount(accounts[0]);
getAllTransactions();
} else {
console.log("No accounts found");
}
} catch (error) {
console.log(error);
}
};
const checkIfTransactionsExists = async () => {
try {
if (ethereum) {
const transactionsContract = createEthereumContract();
const currentTransactionCount = await transactionsContract.getTransactionCount();
window.localStorage.setItem("transactionCount", currentTransactionCount);
}
} catch (error) {
console.log(error);
throw new Error("No ethereum object");
}
};
const connectWallet = async () => {
try {
if (!ethereum) return alert("Please install MetaMask.");
const accounts = await ethereum.request({ method: "eth_requestAccounts", });
setCurrentAccount(accounts[0]);
window.location.reload();
} catch (error) {
console.log(error);
throw new Error("No ethereum object");
}
};
const sendTransaction = async () => {
try {
if (ethereum) {
const { addressTo, amount, keyword, message } = formData;
const transactionsContract = createEthereumContract();
const parsedAmount = ethers.utils.parseEther(amount);
await ethereum.request({
method: "eth_sendTransaction",
params: [{
from: currentAccount,
to: addressTo,
gas: "0x5208", //21,000 gwei in hexadecimal form
value: parsedAmount._hex,
}],
});
const transactionHash = await transactionsContract.addToBlockchain(addressTo, parsedAmount, message, keyword);
setIsLoading(true);
console.log(`Loading - ${transactionHash.hash}`);
await transactionHash.wait();
console.log(`Success - ${transactionHash.hash}`);
setIsLoading(false);
const transactionsCount = await transactionsContract.getTransactionCount();
setTransactionCount(transactionsCount.toNumber());
window.location.reload();
} else {
console.log("No ethereum object");
}
} catch (error) {
console.log(error);
throw new Error("No ethereum object");
}
};
useEffect(() => {
checkIfWalletIsConnect();
checkIfTransactionsExists();
}, [transactionCount]);
return (
<TransactionContext.Provider
value={{
transactionCount,
connectWallet,
transactions,
currentAccount,
isLoading,
sendTransaction,
handleChange,
formData,
}}
>
{children}
</TransactionContext.Provider>
);
};
useFetch.jsx:
import { useEffect, useState } from 'react';
const API_KEY = import.meta.env.VITE_GIPHY_API;
const useFetch = () => {
const [gifUrl, setGifUrl] = useState("");
const fetchGifs = async () => {
try {
const response = await fetch(`https://api.giphy.com/v1/gifs/search?api_key=${API_KEY}&q=${keyword.split(" ").join("")}&limit=1`)
const { data } = await response.json();
setGifUrl(data[0]?.images?.downsized_medium?.url)
} catch (error) {
setGifUrl('https://metro.co.uk/wp-content/uploads/2015/05/pokemon_crying.gif?quality=90&strip=all&zoom=1&resize=500%2C284')
}
}
useEffect(() => {
if (keyword) fetchGifs();
}, [keyword]);
return gifUrl;
}
export default useFetch;
When I comment out the lines that use 'keyword' it launches with no errors. This is at lines 14, 18, and 58-62 of Transactions.jsx.
Any help would be greatly appreciated, thank you!
The problem here is that you have not define keyword inside your useFetch function.
If you are trying to pass the keyword from the place where you use useFetch then do something like below and use the useFetch like const gifUrl = useFetch(<keyword>)
import { useEffect, useState } from 'react';
const API_KEY = import.meta.env.VITE_GIPHY_API;
const useFetch = (keyword) => {
const [gifUrl, setGifUrl] = useState("");
const fetchGifs = async () => {
try {
const response = await fetch(`https://api.giphy.com/v1/gifs/search?api_key=${API_KEY}&q=${keyword.split(" ").join("")}&limit=1`)
const { data } = await response.json();
setGifUrl(data[0]?.images?.downsized_medium?.url)
} catch (error) {
setGifUrl('https://metro.co.uk/wp-content/uploads/2015/05/pokemon_crying.gif?quality=90&strip=all&zoom=1&resize=500%2C284')
}
}
useEffect(() => {
if (keyword) fetchGifs();
}, [keyword]);
return gifUrl;
}
export default useFetch;
or even try adding default value for key work like below.
const useFetch = (keyword = "some keyword") => {