Why my currentRole isn't set? [ReactJS & Firebase] - reactjs

I attempted to set the state of currentRole, but it haven't changed anything when I set in setCurrentRole. At the same time, my console.log() is show the value of doc.data().role. How can I get'em?
import React, {useState, useEffect} from "react";
import firebase, {firestore} from "../utils/firebase";
export const AuthContext = React.createContext();
export const AuthProvider = ({children}) => {
const [loading,setLoading] = useState(true)
const [currentUser, setCurrentUser] = useState(null)
const [currentRole, setCurrentRole] = useState('')
useEffect(() => {
firebase.auth().onAuthStateChanged((user) => {
setCurrentUser(user)
loadingRole(user)
setLoading(false)
console.log(currentRole)
})
}, [])
function loadingRole (user) {
firestore.collection('users').where(firebase.firestore.FieldPath.documentId(), '==', user.uid).get().then((user) => {
user.docs.forEach(doc => {
console.log(doc.data().role)
var roleStr = await doc.data().role
setCurrentRole(roleStr)
})
})
}
if (loading) {
return <p> Loading... </p>
}
return (
<AuthContext.Provider value={{currentUser,currentRole}}>
{children}
</AuthContext.Provider>
)
}
Output:
Auth.js:16 -Have nothing- (console.log(currentRole))
Auth.js:23 Admin (console.log(doc.data().role) line)
LoginComplete.js:14 TgHoVElWCUTViiArtfAKgfNI2J33

Related

Blank page after refresh react firebase authentication

I have the above firebase db. I want to extract the displayName value and use it in a greeting message after the user is successfully login (e.g. Hello George!). I manage to achieve this but when I refresh the page everything disappears and in console I get this error "index.esm2017.js:1032 Uncaught TypeError: Cannot read properties of undefined (reading 'indexOf')".
Is this a problem of how I extract the displayName from firebase document?
Can someone explain to me what is the problem, please?
Here is my code:
AuthContext.js
import { createContext, useContext, useEffect, useState } from "react";
import {
onAuthStateChanged,
signInWithEmailAndPassword,
signOut,
} from "firebase/auth";
import { auth } from "../utils/firebase/firebase.utils";
const UserContext = createContext();
export const AuthContextProvider = ({ children }) => {
const [user, setUser] = useState({});
const signIn = (email, password) =>
signInWithEmailAndPassword(auth, email, password);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
console.log(currentUser);
setUser(currentUser);
});
return () => unsubscribe();
}, []);
const logOut = () => signOut(auth);
return (
<UserContext.Provider value={{ user, signIn, logOut }}>
{children}
</UserContext.Provider>
);
};
export const UserAuth = () => {
return useContext(UserContext);
};
WelcomePage.jsx
import React, { useState, useEffect } from "react";
import { UserAuth } from "../contexts/AuthContext";
import { db } from "../utils/firebase/firebase.utils";
import { doc, Firestore, getDoc } from "firebase/firestore";
const WelcomePage = () => {
const [userDetails, setUserDetails] = useState({});
const { user } = UserAuth();
useEffect(() => {
const docRef = doc(db, "users", user.uid);
const fetchData = async () => {
try {
const docSnap = await getDoc(docRef);
setUserDetails(docSnap.data());
console.log(docSnap.data());
} catch (e) {
console.log(e);
}
};
fetchData();
}, [user]);
return (
<div>
<h1>Hello, {userDetails.displayName}!</h1>
</div>
);
};
export default WelcomePage;
You might only want to fetch documents in the WelcomePage component if there's a truthy uid value to use.
const { user } = UserAuth();
useEffect(() => {
const fetchData = async () => {
const docRef = doc(db, "users", user.uid);
try {
const docSnap = await getDoc(docRef);
setUserDetails(docSnap.data());
console.log(docSnap.data());
} catch (e) {
console.log(e);
}
};
if (user?.id) {
fetchData();
}
}, [user]);

React: setItems Error when using localStorage

When I am using the setItem() Method, i get no errors and everything is working fine. But when I try to retrieve objects via the getItem() Method, im getting the error: "src\components\UserCourses.js Line 33:8: 'setItems' is not defined no-undef"
//LocalStorage
useEffect(() => {
localStorage.setItem('basket', JSON.stringify(basket));
}, [basket]);
useEffect(() => {
const basket = JSON.parse(localStorage.getItem('basket'));
if (basket) {
setItems(basket);
}
}, []);
basket is my empty array at the start, where i put in items.
import { useStateValue } from './StateProvider'
import {useState, useEffect} from 'react'
function UserCourses ({id, name, prof, language}) {
const [{basket}, dispatch] = useStateValue();
const navigate = useNavigate()
const goCourseDetail = () => navigate(`/course/id=${id}`)
const removeFromBasket = () => {
dispatch({
type: 'REMOVE_FROM_BASKET',
id: id,
});
}
useEffect(() => {
localStorage.setItem('basket', JSON.stringify(basket));
}, [basket]);
useEffect(() => {
const basket = JSON.parse(localStorage.getItem('basket'));
if (basket) {
setItems(basket);
}
}, []);
//Provider
import React, {createContext, useContext, useReducer} from 'react';
//data layer
export const StateContext = createContext();
//Provider
export const StateProvider = ({reducer, initialState, children}) => (
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
);
//use inside of a component
export const useStateValue = () => useContext(StateContext);
I had a problem with localStorage in a shopping cart I was developing, if it helps. I was able to solve my problem, with the next code:
const initialState = []
const [carrito, setCarrito] = useState(initialState)
useEffect(() => {
const carritoLS = JSON.parse(localStorage.getItem('carrito'))
if (carritoLS) {
setCarrito(carritoLS)
}
}, [])
useEffect(() => {
if (carrito !== initialState){
localStorage.setItem('carrito', JSON.stringify(carrito))
}
}, [carrito])

Why does my image in Firebase Storage render only after reload 404 error

I am able to upload the image successfully to Firebase Storage, but when I try to render it back to my dashboard I get a 404 error, file not found. I figure it has to do with the database not uploading the image and then sending it back to my react app. When I reload the page, the image renders. I have an async/await function for setting the userImage. What is the best way to manage this? New to React btw.
import { useState, createContext, useEffect } from "react";
import app from "../firebase";
const UserContext = createContext();
const UserProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [userImg, setUserImage] = useState(null);
console.log("userImg from context", userImg);
const img = async () => {
const imgPath = await app.firebase
.storage()
.ref(`users/${user.uid}/profile.jpg`)
.getDownloadURL();
setUserImage(imgPath);
};
useEffect(() => {
if (user) img();
}, [user]);
useEffect(() => {
app.auth().onAuthStateChanged(setUser);
}, [user]);
return (
<UserContext.Provider value={[user, setUser, userImg, setUserImage]}>
{children}
</UserContext.Provider>
);
};
export { UserContext, UserProvider };
The problem with your code is that the component gets rendered before even the img is retrieved from the server.
So what you can do is have a loading state in your component and until the img isn't received back from the server set the loading state to true.
import { useState, createContext, useEffect } from "react";
import app from "../firebase";
const UserContext = createContext();
const UserProvider = ({ children }) => {
const [loading, setLoading] = useState(false) // <--------Loading state--------
const [user, setUser] = useState(null);
const [userImg, setUserImage] = useState(null);
console.log("userImg from context", userImg);
const img = async () => {
setLoading(true) // Setloading to true
const imgPath = await app.firebase
.storage()
.ref(`users/${user.uid}/profile.jpg`)
.getDownloadURL();
setUserImage(imgPath);
setLoading(false) // <--------setting loading to false after receiving the image-------
};
useEffect(() => {
if (user) img();
}, [user]);
useEffect(() => {
app.auth().onAuthStateChanged(setUser);
}, [user]);
if(loading) {
return (<div>Loading...</div>) // <-----return loading if loading is true----
}
return (
<UserContext.Provider value={[user, setUser, userImg, setUserImage]}>
{children}
</UserContext.Provider>
);
};
export { UserContext, UserProvider };

React: Best way to pass Firestore info to components?

Can't figure out an efficient way to set a user's profile and then pass that data onwards to other components as needed.
Below is an example of my current logic, and although the app works, the rendering is not efficient. When I click on various parts of my app, the data coming from my UserProfile component is re-rendering every time causing the text to change from the initial text to the rendered data text.
The main issue, I believe, is the communication between the UserProfile and Dashboard Home snippets below. I'm new to the useEffect logic, so I would imagine the inefficient setup is with that hook.
Any help or nudge in the right direction is appreciated!
Thanks
AuthContext file => Setting the current user
import React, { useContext, useState, useEffect } from 'react';
import firebase from 'firebase/app';
import {
auth,
signInWithGoogle,
createUserProfileDocument,
firestore,
} from '../firebase.utils';
const AuthContext = React.createContext();
export const useAuth = () => {
return useContext(AuthContext);
};
export const AuthProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
const signup = (email, password) => {
return auth.createUserWithEmailAndPassword(email, password);
};
const login = (email, password) => {
return auth.signInWithEmailAndPassword(email, password);
};
const logout = () => {
setCurrentUser(null);
return auth.signOut();
};
const resetPassword = email => {
return auth.sendPasswordResetEmail(email);
};
const updateEmail = email => {
return currentUser.updateEmail(email);
};
const updatePassword = password => {
return currentUser.updatePassword(password);
};
const deleteProfile = () => {
currentUser.delete();
firestore.doc(`users/${currentUser.uid}`).delete();
};
const updateName = displayName => {
return currentUser.updateProfile({
displayName: displayName,
});
};
const setName = displayName => {
return auth.currentUser.updateProfile({
displayName: displayName,
});
};
const googleSignIn = () => {
const google = signInWithGoogle();
setCurrentUser(google);
return google;
};
const updatePersonalSettings = data => {
createUserProfileDocument(currentUser, data);
};
const updateAccountSettings = data => {
createUserProfileDocument(currentUser, data);
};
console.log(currentUser);
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, []);
const value = {
currentUser,
login,
signup,
logout,
resetPassword,
updateEmail,
updatePassword,
updateName,
setName,
googleSignIn,
updatePersonalSettings,
updateAccountSettings,
deleteProfile,
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
};
UserProfile file => Setting the userInfo
import { useState, useEffect } from 'react';
import { useAuth } from '../context/auth-context';
import { createUserProfileDocument } from '../firebase.utils';
const UserProfile = () => {
const { currentUser } = useAuth();
const [userInfo, setUserInfo] = useState();
const [loading, setLoading] = useState(true);
const setUserData = async () => {
if (currentUser) {
const userRef = await createUserProfileDocument(currentUser);
userRef.onSnapshot(doc => {
setUserInfo({
id: doc.id,
...doc.data(),
});
});
}
};
useEffect(() => {
setUserData();
}, []);
return { userInfo };
};
export default UserProfile;
Dashboard home file => Example of rendering data from the UserProfile component
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import sprite from '../../../../assets/sprite.svg';
import UserProfile from '../../../../user-profile/user-profile';
import './home-dashboard.styles.scss';
const HomeDashboard = () => {
const { userInfo } = UserProfile();
const handleCurrentLevel = () => {
return !userInfo || userInfo.currentLevel === undefined ? (
<h1>Welcome! Start your eval to see your level</h1>
) : (
<h1>Current Level: {userInfo.currentLevel}</h1>
);
};
const handleCurrentLevelCard = () => {
return !userInfo || userInfo.currentLevel === undefined
? 'Start a new eval to see your level'
: `You scored a ${userInfo.currentLevel} in your last eval`;
};
return (
<div className="home-dash">
<div className="home-dash__title">{handleCurrentLevel()}</div>
<div className="home-dash__cards">
<div className="home-dash__card-1">
<svg className="icon home-dash__card-icon">
<use href={sprite + '#card-icon-success'}></use>
</svg>
<h3 className="home-dash__card-title">
Latest Eval Results
</h3>
<div className="home-dash__card-result-text">
{handleCurrentLevelCard()}
</div>
<Link to="/eval-quiz">
<button className="home-dash__card-btn--purple">
Start New Eval
</button>
</Link>
</div>
{/* TODO Add resutls to firestore */}
{
<div className="home-dash__card-2">
<svg className="icon home-dash__card-icon">
<use href={sprite + '#card-icon-lightbulb'}></use>
</svg>
<h3 className="home-dash__card-title">
Areas to practice
</h3>
<div className="home-dash__card-result-text">
We recommend working on reading skills
</div>
{/*<button className="home-dash__card-btn--blue">
Practice
</button>*/}
<div className="home-dash__coming-soon">
Coming soon!
</div>
</div>
}
</div>
</div>
);
};
export default HomeDashboard;
Firestore/Firebase setup
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';
const app = firebase.initializeApp({
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER,
appId: process.env.REACT_APP_FIREBASE_APP_ID,
measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID,
});
export const createUserProfileDocument = async (userAuth, additionalData) => {
if (!userAuth) return;
const userRef = firestore.doc(`users/${userAuth.uid}`);
const snapShot = await userRef.get();
const { displayName, email, photoURL } = userAuth;
const createdAt = new Date();
if (!snapShot.exists) {
console.log(displayName);
try {
await userRef.set({
displayName,
photoURL,
email,
createdAt,
...additionalData,
});
} catch (error) {
console.log('error catching data', error.message);
}
}
if (snapShot.exists) {
try {
await userRef.update({
displayName,
email,
...additionalData,
});
} catch (error) {
console.log('error catching data', error.message);
}
}
return userRef;
};
export const firestore = firebase.firestore();
const googleProvider = new firebase.auth.GoogleAuthProvider();
googleProvider.setCustomParameters({ prompt: 'select_account' });
export const signInWithGoogle = () => auth.signInWithPopup(googleProvider);
export const auth = app.auth();
export default app;
Consider this answer a draft, I've made it public in case you can piece it together but I'll update it with some documentation in the morning.
import { useState, useEffect } from 'react';
import { useAuth } from '../context/auth-context';
import { createUserProfileDocument } from '../firebase.utils';
const UserProfile = () => {
const { currentUser } = useAuth();
const [userInfo, setUserInfo] = useState();
const [loading, setLoading] = useState(true);
useEffect(() => {
if (!currentUser) {
// user is signed out
setUserInfo(null);
setLoading(false);
return;
}
const userRef = /* ... */;
setUserInfo(undefined); // clear stale data
setLoading(true); // update loading status
return userRef.onSnapshot({
next(snapshot) {
if (!snapshot.exists) {
userRef
.set({
/* new data */
})
.catch((err) => {
// TODO: Handle errors
});
return;
}
setUserInfo({
id: snapshot.id,
...snapshot.data(),
});
setLoading(false);
},
error(err) {
// TODO: Handle errors
}
});
}, [currentUser]);
return { userInfo }; // <-- this seems odd? missing loading?
};
export default UserProfile;

How to achieve infinite scroll in React and Redux?

import React, {useState, useEffect} from 'react';
import {connect} from 'react-redux';
import {
fetchRecipes
} from '../../store/actions';
import './BeerRecipes.css';
const BeerRecipes = ({recipesData, fetchRecipes}) => {
const [page, setPage] = useState(1);
const [recipes, setRecipes] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchRecipes();
}, [])
return (
<div className='beer_recipes_block'>
<div className='title_wrapper'>
<h2 className='title'>Beer recipes</h2>
</div>
<div className='beer_recipes'>
<ul className='beer_recipes_items'>
{
recipesData && recipesData.recipes && recipesData.recipes.map(recipe =>
<li className='beer_recipes_item' id={recipe.id}>{recipe.name}</li>
)
}
</ul>
</div>
</div>
);
};
const mapStateToProps = state => {
return {
recipesData: state.recipes
}
}
const mapDispatchToProps = dispatch => {
return {
fetchRecipes: () => dispatch(fetchRecipes())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(BeerRecipes);
this is my component where I would like to create infinite scroll and below is my redux-action with axios:
import axios from "axios";
import * as actionTypes from "./actionTypes";
export const fetchRecipesRequest = () => {
return {
type: actionTypes.FETCH_RECIPES_REQUEST
}
}
export const fetchRecipesSuccess = recipes => {
return {
type: actionTypes.FETCH_RECIPES_SUCCESS,
payload: recipes
}
}
export const fetchRecipesFailure = error => {
return {
type: actionTypes.FETCH_RECIPES_FAILURE,
payload: error
}
}
export const fetchRecipes = (page) => {
return (dispatch) => {
dispatch(fetchRecipesRequest)
axios
.get('https://api.punkapi.com/v2/beers?page=1')
.then(response => {
const recipes = response.data;
dispatch(fetchRecipesSuccess(recipes));
})
.catch(error => {
const errorMsg = error.message;
dispatch(fetchRecipesFailure(errorMsg));
})
}
}
I want to create a scroll. I need, firstly, to display first 10 elements and then to add 5 elements with every loading. I have 25 elements altogether and when the list is done it should start from the first five again.

Resources