firebase `onAuthStateChanged` function returns null each time - reactjs

When I'm calling handleAuth function after registeration, a user has been created(I can see him being added to the firebase authentication web), but onAuthStateChanged logs null.
I'm caling it inside useEffect after a user is successfully signed in. console.log(data) inside it returns the user's token from the server response data.
Why's onAuthStateChanged returns null?
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap-icons/font/bootstrap-icons.css';
import { useEffect, useRef, useState } from 'react';
import useFetch from '../hooks/useFetch';
import { useLocation } from 'react-router';
import { auth } from '../config/firebaseConfig';
export default function Authenticate() : JSX.Element {
const url = useLocation().pathname;
const { fetchData, data, error, loading } = useFetch();
const email = useRef() as React.MutableRefObject<HTMLInputElement>;
const password = useRef() as React.MutableRefObject<HTMLInputElement>;
const [show, setShow] = useState(false);
const handleAuth = (e : React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const path = (e.nativeEvent as any).submitter.name === 'login' ?
`${url}/login/email` : `${url}/register`;
fetchData(path, 'POST', {
email : email.current.value,
password : password.current.value
});
}
useEffect(() => {
if(data)
{
console.log(data)
auth.onAuthStateChanged(user => console.log(user));
}
}, [data])
return (
<div className="d-flex justify-content-center align-items-center vh-100 bg-white">
<div className="p-5 border border-2 border-success">
<h1 className="fw-light fs-2 text-center">Authenticate</h1>
<br/>
<form onSubmit={e => handleAuth(e)}>
<div className="form-group">
<label>Email</label>
<div className="input-group">
<span className="input-group-text bi bi-person-circle"></span>
<input type="email" ref={email} className="form-control" placeholder="boomer#bommer.com" required/>
</div>
</div>
<div className="form-group">
<label>Password</label>
<div className="input-group">
<span className="input-group-text bi bi-key-fill"/>
<input type={show ? "text" : "password"} className="form-control" ref={password} placeholder="enter your password" required/>
<button type="button" className={`input-group-text text-decoration-none bi bi-${show ? 'eye-fill' : 'eye-slash-fill' }`} onClick={() => setShow(!show)}/>
</div>
</div>
<br/>
{ error && <p className={`alert alert-danger p-1 text-center`} role="alert">{error}</p> }
<div className="d-flex justify-content-between">
<button type="submit" name="register" className="btn btn-primary" disabled={loading}>Register</button>
<button type="submit" name="login" className="btn btn-primary" disabled={loading}>Login</button>
</div>
</form>
</div>
</div>
);
}

The creation of the user is different from the authentication of the use.
While creating the user, you will storing new user credentials (email / password), while when authenticating the user you will matching the provided credentials against the stored ones.
The listener onAuthStateChanged seems to be well installed but you are not engaging any authentication so that it submits a new values: the authenticated user.
Once the user has been successfully created, and given his account has been validated, you can trigger a sign in request as follows:
const handleAuth = (e : React.FormEvent<HTMLFormElement>) => {
// ...
fetchData(path, 'POST', {
email : email.current.value,
password : password.current.value
}).then(() => auth.signInWithEmailAndPassword(email.current.value, password.current.value))
}
Then the registered onAuthStateChanged listener will get triggered with the proper user identity.

Related

My firebase Create User trigger 2 times instead of one

Creating a react app usign react-firebase-hook,firebase,react-hook-form,daisyUI
The problem is When I am pressing submit button it triggers 2 times in the console and when using the Button; Sign Up With Google it triggers 3times.Thanks for your time in advance.
In SingUP function:
import auth from '../../firebase.init';
import { useSignInWithGoogle } from 'react-firebase-hooks/auth';
import { useForm } from "react-hook-form";
import { useCreateUserWithEmailAndPassword } from 'react-firebase-hooks/auth';
function SignUp() {
const [signInWihGoogle, googleUser, googleLoading, googleError] = useSignInWithGoogle(auth);
const { register, formState: { errors }, handleSubmit } = useForm();
const [
createUserWithEmailAndPassword,
user,
loading,
error,
] = useCreateUserWithEmailAndPassword(auth);
This function is triggering:
const onSubmit = data =>{
createUserWithEmailAndPassword(data.email,data.password);
alert('You successfully created your account');
}
<form onSubmit={handleSubmit(onSubmit)}>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Name</span>
</label>
<input
type="text"
placeholder="Pls type name here"
class="input input-bordered w-full max-w-xs"
{...register("name", {
required:{
value: true,
message:'Name is Required'
},
minLength: {
value:2,
message:'Name must be 2 or more characters
}
})}/>
</div>
... .... ...
<input
type='submit'
value= 'Signup'
class="btn w-full max-w-xs text-white"/>
</form>
<p className='text-center text-xs pt-1'>Already have an account?<Link
className='text-secondary' to='/login'> Please Login</Link></p>
<div class="divider">OR</div>
<button
onClick={() => signInWihGoogle()}
class="btn btn-outline">Continue with Google</button>
</div>
Do you still have the strict mode tag in your App?
If so: delete it and if you want you can read more on it here: https://reactjs.org/docs/strict-mode.html

Why doesn't my React app redirect after login?

I'm new to React and Typescript and what I'm trying to do is, after successfully logging in I want to redirect the user to the homepage, but navigate doesn't seem to work.
Here is my login component:
function Login() {
const auth = useRecoilValue(authAtom);
const { register, handleSubmit, formState } = useForm<IFormValues>();
const navigate = useNavigate();
const { isSubmitting } = formState;
console.log(isSubmitting);
function onSubmit(values: IFormValues ) {
const baseUrl = `${process.env.REACT_APP_API_URL}/users/authenticate`;
const creds = {
Username: values.username,
Password: values.password
};
return authenticateApi(baseUrl, creds)
.then(X => {
navigate('/');
});
}
useEffect(() => {
// redirect to home if already logged in
if (auth) navigate('/');
}, []);
return (
<div className="col-md-6 offset-md-3 mt-5">
<div className="card">
<h4 className="card-header">Login</h4>
<div className="card-body">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form-group">
<label>Username</label>
<input type="text" {...register("username")} className={`form-control`} />
<div className="invalid-feedback"></div>
</div>
<div className="form-group">
<label>Password</label>
<input type="password" {...register("password")} className={`form-control`} />
<div className="invalid-feedback"></div>
</div>
<button disabled={isSubmitting} className="btn btn-primary">
{isSubmitting && <span className="spinner-border spinner-border-sm mr-1"></span>}
Login
</button>
</form>
</div>
</div>
</div>
)
}
I have been stuck on this for a while, so any help would be greatly appreciated.
Do you ever actually import useNavigate()? It comes from the react-router-dom package, and can be used like so:
import { useNavigate } from 'react-router-dom';
function Login() {
// ...
const navigate = useNavigate();
// ...
}
Try this one it might work! or you can create a new function to redirect user to homepage after login
import { useNavigate } from "react-router-dom";
function Login() {
//This is for navifating user to home page
const navigate = useNavigate();
const auth = useRecoilValue(authAtom);
const { register, handleSubmit, formState } = useForm<IFormValues>();
const { isSubmitting } = formState;
console.log(isSubmitting);
const onSubmit = (values: IFormValues ) => {
const baseUrl = `${process.env.REACT_APP_API_URL}/users/authenticate`;
const creds = {
Username: values.username,
Password: values.password
};
return authenticateApi(baseUrl, creds)
.then(X => {
navigate('/');
});
}
const auth = () => {
// redirect to home if already logged in
navigate('/');
};
return (
<div className="col-md-6 offset-md-3 mt-5">
<div className="card">
<h4 className="card-header">Login</h4>
<div className="card-body">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form-group">
<label>Username</label>
<input type="text" {...register("username")} className={`form-control`} />
<div className="invalid-feedback"></div>
</div>
<div className="form-group">
<label>Password</label>
<input type="password" {...register("password")} className={`form-control`} />
<div className="invalid-feedback"></div>
</div>
<button disabled={isSubmitting} className="btn btn-primary">
{isSubmitting && <span className="spinner-border spinner-border-sm mr-1"></span>}
Login
</button>
</form>
</div>
</div>
</div>
)
}

e.preventDefault() not working in my react app which is causing unnessary redirect

Hi I am trying to create a react-redux form and my e.preventDefault() is not working which i causing a redirect everytime I press an enter key.
Could someone look in my code and tell me where am I going wrong:
I am having an input field and when I am giving an e.preventDefault(), but for some reason its redirecting.
import React, { useState } from "react";
import { useDispatch } from "react-redux";
function InputField() {
let [task, setTask] = useState("");
const dispatch = useDispatch();
let onTaskAdd = (e) => {
setTask(e.target.value);
};
let addTaskinTaskManager = () => {
dispatch({ type: "ADD_TASK", payload: task });
setTask("");
};
return (
<div className="container-fluid mt-5">
<div className="row justify-content-center">
<div className="col-md-6">
<form>
<div className="form-group row">
<label className="col-md-2 col-form-label">Task:</label>
<div className="col-md-10">
<input
type="text"
id="task"
name="task"
className="form-control"
placeholder="eg, singing"
value={task}
onChange={(e) => {
e.preventDefault();
e.stopPropagation();
onTaskAdd(e);
}}
/>
</div>
</div>
</form>
</div>
<div className="col-md-2">
<button
type="button"
onClick={addTaskinTaskManager}
className="btn btn-primary"
>
Add Task
</button>
</div>
</div>
</div>
);
}
export default InputField;

ReactJS: Redirect after form submit without refresh

i have created a form which calls the search function after pressing submit button. I want the results of the search to be displayed in another function component (have used React Context for this purpose).
However, i faced an issue where i cannot link it to another route /yourstage/results WITHOUT refreshing the page. If the page is refreshed, my seachResult stored in state will be gone.
import React, { Fragment, useState, useEffect, useContext } from "react";
import { withRouter, Link } from "react-router-dom";
import searchResultContext from "./SearchResultContext";
const Search = () => {
const [wave, setWave] = useState("$perc");
const [pack_hu, setPackHu] = useState("$perc");
const { searchResult, setSearchResult } = useContext(searchResultContext);
useEffect(() => {
console.log(JSON.stringify(searchResult));
}, [searchResult]);
//submit a form to search
const submitSearch = async (e) => {
e.preventDefault()
try {
const response = await fetch(
`http://localhost:5000/yourstage/search/${wave}/${pack_hu}`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
}
);
setSearchResult(await response.json());
//Reset Form and State
document.getElementById("searchForm").reset();
setWave("$perc");
setPackHu("$perc");
} catch (error) {
console.error(error.message);
}
};
//return the form html
return (
<Fragment>
<h1 className="text-center mt-3">Search</h1>
<form id="searchForm" onSubmit={submitSearch}>
<div className="form-group row">
<label htmlFor="Wave" className="col-sm-2 col-form-label">
Wave:
</label>
<div className="col-sm-10">
<input
type="text"
className="form-control"
placeholder="Wave Number"
maxLength="10"
onChange={(e) => setWave(e.target.value)}
/>
</div>
</div>
<div className="form-group row">
<label htmlFor="pack_hu" className="col-sm-2 col-form-label">
Pack HU:
</label>
<div className="col-sm-10">
<input
type="text"
className="form-control"
placeholder="Pack HU"
maxLength="10"
onChange={(e) => setPackHu(e.target.value)}
/>
</div>
</div>
<div className="row text-center mt-5">
{/**Search based on parameter*/}
<div className="col-6">
<button type="submit" className="btn-lg btn-primary">
<Link to="/yourstage/results">Search</Link>
</button>
</div>
</div>
</form>
</Fragment>
);
};
export default withRouter(Search);

React: POST API call embedding wrong path

I created my project using CRA react CLI and I have stored data in a local JSON file in the public folder.
I have a Login Component. This is where I am making all the POST API calls using Axios and I've also have some other stuff. This component is later being imported inside Auth Layout.
The problem is: On submit, it is sending the POST request to the wrong path. It should send to /data/api/v1/login.json instead it is sending to http://localhost:3000/auth/data/api/v1/login.json.
I think this could be since the login component is loading from the auth layout. but, not sure how to resolve it.
Login component
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
//api calls
import axios from "axios";
import { setUserSession } from "../../Common/Utils/Common.js";
import { Button, Form, Input, InputGroup, InputGroupAddon } from "reactstrap";
const Login = (props) => {
const [loading, setLoading] = useState(false);
const username = useFormInput("");
const password = useFormInput("");
const [error, setError] = useState(null);
const handleLogin = () => {
setError(null);
setLoading(true);
axios
.post("data/api/v1/login.json", {
username: username.value,
password: password.value,
})
.then((response) => {
console.log("response", response);
setLoading(false);
setUserSession(response.data.token, response.data.user);
props.history.push("/admin/dashboard");
})
.catch((error) => {
setLoading(false);
if (error.response.status === 401)
setError(error.response.data.message);
else setError("Something went wrong. Please try again later.");
});
};
return (
<div className="container-fluid backgroundContainer">
<div className="Login">
<div className="login-form-container">
<div className="logo">
<img src={Logo} className="App-logo" alt="logo" />
</div>
<div className="content">
<Form className="login-form">
<h3 className="form-title">Welcome</h3>
<InputGroup>
<InputGroupAddon
className="input-group-addon"
addonType="prepend"
>
<i className="fa fa-user"></i>
</InputGroupAddon>
<Input
autoFocus
type="email"
aria-label="Username"
aria-describedby="username"
aria-invalid="false"
placeholder="Username or Email"
{...username}
/>
</InputGroup>
<InputGroup>
<InputGroupAddon
className="input-group-addon"
addonType="prepend"
>
<i className="fa fa-lock"></i>
</InputGroupAddon>
<Input
value={password}
placeholder="Password"
aria-label="password"
aria-describedby="password"
{...password}
// onChange={(e) => setPassword(e.target.value)}
type="password"
/>
</InputGroup>
<div className="form-actions">
{error && (
<>
<small style={{ color: "red" }}>{error}</small>
<br />
</>
)}
<br />
<button
className="pull-right"
block="true"
type="submit"
value={loading ? "Loading..." : "Login"}
onClick={handleLogin}
disabled={loading}
>
Login
</button>
<br />
</div>
<div className="forgotPassword">
<Link to="/auth/resetpassword">Forgot password?</Link>
</div>
</Form>
</div>
</div>
</div>
</div>
);
};
const useFormInput = (initialValue) => {
const [value, setValue] = useState(initialValue);
const handleChange = (e) => {
setValue(e.target.value);
};
return {
value,
onChange: handleChange,
};
};
export default Login;
Auth layout
import React from "react";
import Login from "../../components/pages/login/Login";
class Pages extends React.Component {
render() {
return (
<div className="wrapper wrapper-full-page" ref="fullPages">
<div className="full-page">
<Login {...this.props}></Login>
</div>
</div>
);
}
}
export default Pages;
Common.js this is a common service using in the login component.
// return the user data from the session storage
export const getUser = () => {
const userStr = sessionStorage.getItem('user');
if (userStr) return JSON.parse(userStr);
else return null;
}
// return the token from the session storage
export const getToken = () => {
return sessionStorage.getItem('token') || null;
}
// remove the token and user from the session storage
export const removeUserSession = () => {
sessionStorage.removeItem('token');
sessionStorage.removeItem('user');
}
// set the token and user from the session storage
export const setUserSession = (token, user) => {
sessionStorage.setItem('token', token);
sessionStorage.setItem('user', JSON.stringify(user));
}
You need the leading slash in your axios call, Change
axios.post("data/api/v1/login.json", {
username: username.value,
password: password.value,
})
to
axios.post("/data/api/v1/login.json", {
username: username.value,
password: password.value,
})
This will fix the problem with it adding the sub-path to your api call.

Resources