How to receive user name and country with auth().createUserWithEmailAndPassword - javascript

Good day Developers. Please i am new to react native and i want to use firebase auth for a project. But i don't know how to accept user name and country with the normal auth().createUserWithEmailAndPassword method. Below is my code so far.. But it does work fine.
import React, {createContext, useState} from 'react';
import { StyleSheet, ActivityIndicator, View, Text, Alert } from 'react-native'
import auth from '#react-native-firebase/auth';
import firebase from '#react-native-firebase/app';
export const AuthContext = createContext();
export const AuthProvider = ({children}) => {
const [user, setUser] = useState(null);
return (
<AuthContext.Provider
value={{
user,
setUser,
login: async (email, password) => {
try {
await auth().signInWithEmailAndPassword(email, password);
} catch (e) {
console.log(e);
Alert.alert(
e.message);
}
},
register: async (name, email, password) => {
try {
await auth().createUserWithEmailAndPassword(email, password);
firebase.auth().currentUser.updateProfile({
displayName: name
});
Alert.alert("Success ✅", "Account created successfully")
return {};
}
catch (e) {
Alert.alert(
e.message );
}
},
forgot: async (email) => {
try {
await firebase.auth().sendPasswordResetEmail(email);
Alert.alert("Success ✅", "A Password Recorvery Link has been sent to your mail.")
}
catch (e) {
Alert.alert(
e.message );
}
},
logout: async () => {
try {
await auth().signOut();
} catch (e) {
Alert.alert(
e.message );
}
},
sendver: async () => {
try {
await firebase.auth().currentUser.sendEmailVerification();
} catch (e) {
Alert.alert(
e.message );
}
},
}}>
{children}
</AuthContext.Provider>
);
};
I will really appreaciate if anyone can help. thanks so much.

Try this way
firebase.auth().signInWithEmailAndPassword("abc#gmail.com", "******")
.then(function(user) {
// Success
})
.catch(function(error) {
// Error Handling
});
Note: Save user info on success in your database

Related

Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. - useEffect()

I get this error when I try and call a function I have imported within my useEffect() hook in Dashboard.jsx. I am just trying to pull in data from database on the page load pretty much so that when user click button they can send off correct credentials to the api.
I am pulling it in from database for security reasons, so client id is not baked into the code.
I am pretty sure that I am getting this error maybe because the function is not inside a react component? although I am not 100% sure. And if that is the case I am not sure of the best way to restructure my code and get the desired output.
Code below.
mavenlinkCredentials.js
import { doc, getDoc } from "firebase/firestore";
import { useContext } from "react";
import { AppContext } from "../../context/context";
import { db } from "../../firebase";
const GetMavenlinkClientId = async () => {
const {setMavenlinkClientId} = useContext(AppContext)
const mavenlinkRef = doc(db, 'mavenlink', 'application_id');
const mavenlinkDocSnap = await getDoc(mavenlinkRef)
if(mavenlinkDocSnap.exists()){
console.log("mavenlink id: ", mavenlinkDocSnap.data());
console.log(mavenlinkDocSnap.data()['mavenlinkAccessToken'])
setMavenlinkClientId(mavenlinkDocSnap.data()['application_id'])
} else {
console.log("No doc");
}
}
export default GetMavenlinkClientId;
Dashboard.jsx
import React, { useContext, useEffect, useState } from "react";
import { useAuthState } from "react-firebase-hooks/auth";
import { useNavigate } from "react-router-dom";
import { query, collection, getDocs, where, setDoc, doc, getDoc } from "firebase/firestore";
import { auth, db, logout } from "../firebase";
import { Button, Container, Grid, Paper } from "#mui/material";
import ListDividers from "../components/ListDividers";
import { AppContext } from "../context/context";
import axios from "axios";
import {SuccessSnackbar, ErrorSnackbar} from '../components/PopupSnackbar';
import GetMavenlinkClientId from "../helpers/firebase/mavenlinkCredentials";
const Dashboard = () => {
const [user, loading, error] = useAuthState(auth);
const [name, setName] = useState("");
const [ accessToken, setAccessToken ] = useState("")
const [errorAlert, setErrorAlert] = useState(false);
const [successAlert, setSuccessAlert] = useState(false);
const [mavenlinkClientId, setMavenlinkClientId] = useState("");
const {isAuthenticated} = useContext(AppContext);
const navigate = useNavigate();
const uid = user.uid
const parsedUrl = new URL(window.location.href)
const userTokenCode = parsedUrl.searchParams.get("code");
const { mavenlinkConnected, setMavenlinkConnected } = useContext(AppContext)
const { maconomyConnected, setMaconomyConnected } = useContext(AppContext)
const { bambooConnected, setBambooConnected } = useContext(AppContext)
const fetchUserName = async () => {
try {
const q = query(collection(db, "users"), where("uid", "==", user?.uid));
const doc = await getDocs(q);
const data = doc.docs[0].data();
setName(data.name);
} catch (err) {
console.error(err);
alert("An error occured while fetching user data");
}
};
//
useEffect(() => {
if (loading) return;
if (!user) return navigate("/");
fetchUserName();
if(userTokenCode !== null){
authorizeMavenlink();
}
if(isAuthenticated){
GetMavenlinkClientId()
}
}, [user, loading]);
///put this into a page load (use effect maybe) so user does not need to press button to connect to apis
const authorizeMavenlink = () => {
console.log(uid);
const userRef = doc(db, 'users', uid);
axios({
//swap out localhost and store in variable like apitool
method: 'post',
url: 'http://localhost:5000/oauth/mavenlink?code='+userTokenCode,
data: {}
})
.then((response) => {
setAccessToken(response.data);
setDoc(userRef, { mavenlinkAccessToken: response.data}, { merge: true });
setMavenlinkConnected(true);
setSuccessAlert(true);
})
.catch((error) => {
console.log(error);
setErrorAlert(true)
});
}
//abstract out client id and pull in from db
const getMavenlinkAuthorization = () => {
window.open('https://app.mavenlink.com/oauth/authorize?client_id='+mavenlinkClientId+'&response_type=code&redirect_uri=http://localhost:3000');
window.close();
}
const authorizeBamboo = () => {
axios({
method: 'get',
url: 'http://localhost:5000/oauth/bamboo',
data: {}
})
.then((response) => {
console.log(response)
})
.catch((error) => {
console.log(error);
});
// console.log('bamboo connected')
setBambooConnected(true);
}
const authorizeMaconomy = () => {
console.log("Maconomy connected")
setMaconomyConnected(true);
}
const syncAccount = async() => {
if(!mavenlinkConnected){
await getMavenlinkAuthorization()
}
if (!bambooConnected){
await authorizeBamboo();
}
if (!maconomyConnected){
await authorizeMaconomy();
}
}
const handleAlertClose = (event, reason) => {
if (reason === 'clickaway') {
return;
}
setSuccessAlert(false) && setErrorAlert(false);
};
console.log(mavenlinkClientId);
return(
<>
<Container>
<div className="dashboard">
<h1>Dashboard</h1>
<Grid container spacing={2}>
<Grid item xs={12}>
<Paper style={{paddingLeft: "120px", paddingRight: "120px"}} elevation={1}>
<div className="dashboard-welcome">
<h2>Welcome {name}</h2>
<h4>{user?.email}</h4>
<hr/>
<h2>Integrations</h2>
<Button onClick={syncAccount}>
Sync Account
</Button>
{/* <Button onClick={getMavenlinkClientId}>
Bamboo Test
</Button> */}
<ListDividers/>
</div>
</Paper>
</Grid>
</Grid>
</div>
{successAlert === true ? <SuccessSnackbar open={successAlert} handleClose={handleAlertClose}/> : <></> }
{errorAlert === true ? <ErrorSnackbar open={errorAlert} handleClose={handleAlertClose}/> : <></> }
</Container>
</>
);
}
export default Dashboard;
the error is because you’re calling const {setMavenlinkClientId} = useContext(AppContext) inside the file mavenlinkCredentials.js which is not a react components.
you could maybe change the function inside mavenlinkCredentials.js to accept a setMavenlinkClientId and pass it from outside like this.
const GetMavenlinkClientId = async (setMavenlinkClientId) => {
const mavenlinkRef = doc(db, 'mavenlink', 'application_id');
const mavenlinkDocSnap = await getDoc(mavenlinkRef)
if(mavenlinkDocSnap.exists()){
console.log("mavenlink id: ", mavenlinkDocSnap.data());
console.log(mavenlinkDocSnap.data()['mavenlinkAccessToken'])
setMavenlinkClientId(mavenlinkDocSnap.data()['application_id'])
} else {
console.log("No doc");
}
}
and then you can call this function in your dashboard.js like so,
const {setMavenlinkClientId} = useContext(AppContext)
if(isAuthenticated){
GetMavenlinkClientId(setMavenlinkClientId)
}

How to include username in firebase's 'createUserWithEmailAndPassword' authentication method

I'm trying to make a Username and Password Authentication in a web app that is made with react and firebase.
But I'm really new to firebase and I couldn't find any good documentation about the process, I have read about Firestore but I don't know how to connect it to the authentication method. Any help or hint is appreciated!
Here is the signup function of my /register route.
async function handleSubmit(e) {
e.preventDefault()
if (passwordRef.current.value !== passwordConfirmationRef.current.value){
return setError('Passwords do not match');
}
try {
setError('')
setLoading(true)
await signup(emailRef.current.value, passwordRef.current.value, usernameRef.current.value)
Home()
} catch {
setError('Failed to create an account')
}
setLoading(false)
}
And this is my 'AuthContext.JSX' code:
import React, { useContext , useEffect, useState } from 'react'
const AuthContext = React.createContext()
import {auth} from '../firebase'
export function useAuth() {
return useContext(AuthContext)
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState()
const [loading, setLoading] = useState(true)
function signup(email, password) {
return auth.createUserWithEmailAndPassword(email, password)
}
function login(email, password) {
return auth.signInWithEmailAndPassword(email, password)
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user)
setLoading(false)
})
return unsubscribe
}, [])
auth.onAuthStateChanged(user => {
setCurrentUser(user)
})
const value = {
currentUser,
signup,
login
}
return (
<AuthContext.Provider value={value}>
{!loading && children }
</AuthContext.Provider>
)
}
Just to let you know the authentication is working well the only problem is getting the Display Name and exporting it to the home page for display.
I want to display the username on this page
Thanks to Dhamaraj I used the updateProfile() function inside my Register route, and it worked properly for adding a display name to the user which I used on the home page for the profile display.
That is the new function, may it helps somebody with the same issue:
async function handleSubmit(e) {
e.preventDefault()
if (passwordRef.current.value !== passwordConfirmationRef.current.value){
return setError('Passwords do not match');
}
try {
setError('')
setLoading(true)
await signup(emailRef.current.value, passwordRef.current.value)
auth.currentUser.updateProfile({
displayName: usernameRef.current.value
}).then(() => {
console.log('Username is: ' + auth.currentUser.displayName)
}).catch((error) => {
console.log('An error was occured while updating the profile.')
});
Home()
} catch {
setError('Failed to create an account')
}
setLoading(false)
}

React Native trigger useEffect from changes in AsyncStorage

So I'm working on a react native authentication screen. I'm storing the token in AsyncStorage (yea I know it's not the best solution).
So what's happening is when I log in, the token is stored, but the getItem on my Authentication.js screen is not being triggered, and the profile screen is not being called.
If I log in and then manually refresh the app, I am redirected to the profile screen.
Login.js
function Login({navigation}) {
const [signIn, {data}] = useMutation(USER_SIGNIN_MUTATION);
const [userName, setUserName] = useState('');
const [password, setPassword] = useState('');
function handleLogIn() {
signIn({
variables: {
email: userName,
password: password,
},
});
}
useEffect(() => {
if (data != null) {
setToken();
}
});
const setToken = async () => {
try {
console.log('before');
await AsyncStorage.setItem('token', data.signIn.token);
console.log('after');
} catch (error) {
console.log(error);
}
};
return(
...
)
}
Authentication.js
function Authentication() {
const [localToken, setLocalToken] = useState(false);
useEffect(() => {
const fetchUser = async () => {
try {
console.log('before get');
const userData = await AsyncStorage.getItem('token');
if (userData !== null) {
setLocalToken(true);
}
} catch (error) {
console.log(error);
}
};
fetchUser();
}, [localToken]);
console.log(`auth screen - ${localToken}`);
return (
<NavigationContainer>
{localToken === true ? <ProfileStack /> : <AuthStack />}
</NavigationContainer>
);
}
export default Authentication;
also same happens with the logout function when fired. (the function runs, but I need to refresh the app to get back to the login screen)
Profile.js
function Profile({navigation}) {
function signOut() {
logOut();
}
const logOut = async () => {
try {
console.log('before clear');
await AsyncStorage.removeItem('token');
console.log('after clear');
} catch (error) {
console.log(error);
}
};
return (
...
)
}
I'm grateful for any insight on this.
useEffect(() => {
const fetchUser = async () => {
try {
console.log('before get');
const userData = await AsyncStorage.getItem('token');
if (userData !== null) {
setLocalToken(true);
}
} catch (error) {
console.log(error);
}
};
fetchUser();
}, [localToken]);
Here you added the localToken variable in the dependency array of the useEffect. So you are basically saying: run this effect only if the localToken variable changes. But you change that from within the effect only. So try to remove it and keep the dependency as []. This way the effect will run when the component is rendered.
About the fact that you have to refresh the page, it is important to understand why this happens.
<NavigationContainer>
{localToken === true ? <ProfileStack /> : <AuthStack />}
</NavigationContainer>
Here you are rendering ProfileStack or AuthStack based on the localToken value. When you logout, you remove the token from the AsyncStorage but this is not enough. You actually need to trigger a rerender in the Authentication component so the localToken is reevaluated. Basically, when you logout you also need to set setLocalToken(false). So you need to access setLocalToken function from the Profile component. You can pass this function as a prop or better you can use Context API

Why does the user getting redirected to the login page on refresh?

Okay, there's this simple REACTJS app, where firebase is used.
There once you login everything works fine except when you hit the refresh icon. The moment you do it, it redirects you to the previous place where you were asked to login. That's the problem that this newly-born coder is trying to solve!
I can give you following snippets of code:
This is of the landing page
function Landing() {
const [{ }, dispatch] = useStateValue();
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE)
// .then(function () {
// console.log("successfully set the persistence");
// return firebase.auth().signInWithPopup(provider);
// })
.catch(function (error) {
console.log("failed to ser persistence: " + error.message)
});
firebase.auth().onAuthStateChanged((user) => {
if (user) {
console.log('user is logged in');
} else {
console.log('user is logged out now')
}
});
const signIn = () => {
auth
.signInWithPopup(provider)
.then((result) => {
dispatch({
type: actionTypes.SET_USER,
user: result.user
})
}).catch((error) => alert(error.message))
}
reducer.js snippet
export const initialState = {
user: null,
}
export const actionTypes = {
SET_USER: 'SET_USER',
LOGOUT_USER: 'LOGOUT_USER'
}
const reducer = (state, action) => {
console.log(action)
switch (action.type) {
case actionTypes.SET_USER:
return {
...state,
user: action.user,
}
case actionTypes.LOGOUT_USER:
return {
...state,
user: null,
}
default:
return state;
This is of firebase.js
Yes, Google Authentication is what's being used here
import firebase from 'firebase';
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
//config
};
// const user = firebase.auth().currentUser;
// console.log(user);
const firebaseApp = firebase.initializeApp(firebaseConfig)
const db = firebaseApp.firestore();
const storage = firebase.storage();
const auth = firebaseApp.auth();
const provider = new firebase.auth.GoogleAuthProvider();
export default db;
export { auth, provider, storage }
Finally here is of the app.js
function App() {
const [{ user }, dispatch] = useStateValue();
console.log(user);
return (
<div className="app">
{!user ? (
<Landing />
) : (
<App />
)
</div>
Your attention to this matter is greatly appreciated!
Oh by the way this following question is also related to this. It might help you to get a better idea of this issue. So make sure to take a look at that as well!
How can you persist a logged-in user with firebase?
Thanks again!
sample code. work
import firebase from 'firebase/app';
import 'firebase/auth';
import { useEffect, useState } from 'react';
import firebaseConfig from './firebase-config';
const firebaseApp = firebase.initializeApp(firebaseConfig);
const googleProvider = new firebase.auth.GoogleAuthProvider();
firebaseApp.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION)
.then(function () {
// return firebaseApp.auth().signInWithPopup(googleProvider)
})
.catch(function (error) {
console.log(error)
});
function App() {
const [user, setUser] = useState(null)
useEffect(() => {
firebaseApp.auth().onAuthStateChanged((res) => {
console.log("onAuthStateChanged", res)
if (res) {
setUser(res)
// console.log('user is logged in', user);
} else {
setUser(null)
// console.log('user is logged out now')
}
});
}, [])
const signInWithGoogle = (e) => {
firebaseApp.auth()
.signInWithPopup(googleProvider)
.then((result) => {
// console.log(result)
// setUser(result.additionalUserInfo)
}).catch(err => {
// console.log(err)
})
}
const signOut = (e) => {
firebaseApp.auth().signOut()
}
return (
<div>
<h1>Firebase Authentication</h1>
{
user
? (
<div>
<p>Hello, {user.displayName}</p>
<button onClick={signOut}>Sign out</button>
</div>
)
: (
<div>
<p>Please sign in.</p>
<button onClick={signInWithGoogle}>Sign in with Google</button>
</div>
)
}
</div>
);
}
export default App;

Timeout for RefreshView in React Native Expo App

My current React Native Expo app has a ScrollView that implements RefreshControl. A user pulling down the ScrollView will cause the onRefresh function to be executed, which in turns call an action creator getSpotPrices that queries an API using axios.
Problem: If there is a network problem, the axios.get() function will take very long to time out. Thus, there is a need to implement the timing out of either axios.get() or onRefresh.
How can we implement a timeout function into RefreshControl?
/src/containers/main.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { ScrollView, RefreshControl } from 'react-native';
import MyList from '../components/MyList';
import { getSpotPrices } from '../actions';
class RefreshableList extends Component {
onRefresh = () => {
this.props.getSpotPrices();
}
render() {
return (
<ScrollView
refreshControl={
<RefreshControl
refreshing={this.props.isLoading}
onRefresh={this._onRefresh}
/>
}>
<MyList />
</ScrollView>
)
}
}
const mapStateToProps = (state) => {
return {
isLoading: state.currencies.isLoading,
}
}
const mapDispatchToProps = (dispatch) => {
return {
getSpotPrices: () => dispatch(getSpotPrices()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(RefreshableList);
/src/actions/index.js
import api from "../utils/api";
import * as types from "../types";
import Axios from "axios";
const getSpotPrice = async () => {
try {
const res = await Axios.get(`https://api.coinbase.com/v2/prices/spot`);
return parseFloat(res.data.data.amount);
} catch (err) {
throw new Error(err);
}
};
export const getSpotPrices = () => async dispatch => {
try {
const price = await getSpotPrice();
dispatch({
type: types.CURRENCIES_SET,
payload: price
});
} catch (err) {
dispatch({
type: types.CURRENCIES_FAILED_FETCH,
payload: err.toString()
});
} finally {
dispatch({
type: types.CURRENCIES_IS_LOADING,
payload: false
})
}
};
/src/reducers/currencies.js
import * as types from "../types";
const initialState = {
data: {},
isLoading: false,
};
export default (state = initialState, { type, payload }) => {
switch (type) {
case types.CURRENCIES_SET:
return {
...state,
data: payload,
error: "",
isLoading: false
};
case types.CURRENCIES_FAILED_FETCH:
return {
...state,
error: payload,
isLoading: false
};
case types.CURRENCIES_IS_LOADING:
return {
isLoading: payload
}
default:
return state;
}
};
Check if user is connected internet or not using the react-native-netinfo library
NetInfo.fetch().then(state => {
console.log("Connection type", state.type);
console.log("Is connected?", state.isConnected);
this.setState({ connected: state.isConnected });
});
// Subscribe
const unsubscribe = NetInfo.addEventListener(state => {
console.log("Connection type", state.type);
this.setState({ connected: state.isConnected });
});
// Unsubscribe
unsubscribe(); <- do this in componentwillunmount
Its generally a good practice to add a timeout, in all your api calls, in axios you can easily add a timeout option like:
await axios.get(url, { headers, timeout: 5000 })
so in your case modify the axios call as
await Axios.get(https://api.coinbase.com/v2/prices/spot, { timeout: 5000 } );
I have put timeout of 5 seconds you can modify the parameter according to your need.

Categories

Resources