// I am working on Login/Register from in React and I am using firebase auth for
authentication. When the user logged in I want to redirect the user on the root path or
on App component. But I got into an infinite loop which gives me this error (#Throttling
navigation to prevent the browser from hanging.#)//
**App Js**
import { useEffect } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import "./App.css";
import Header from "./components/Header";
import Home from "./components/Home";
import Login from "./components/Login";
import { getUserAuth } from "./actions";
import { connect } from "react-redux";
function App(props) {
useEffect(() => {
props.getUserAuth();
}, []);
return (
<div className="App">
<BrowserRouter>
<Routes>
<Route exact path="/" element={<Login />} />
<Route
path="/home"
element={
<>
<Header />
<Home />
</>
}
/>
</Routes>
</BrowserRouter>
</div>
);
}
const mapStateToProps = (state) => {
return {};
};
const mapDispatchToProps = (dispatch) => ({
getUserAuth: () => dispatch(getUserAuth()),
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
//App Section End//
**LogIn**
import styled from "styled-components";
import { connect } from "react-redux";
import { signInAPI } from "../actions";
import { Navigate } from "react-router-dom";
const Login = (props) => {
return (
<Container>
{props.user && <Navigate to="/home" />}
<Nav>
<a href="/">
<img src="/images/login-logo.svg" alt="" />
</a>
<div>
<Join>Join now</Join>
<SignIn>Sign in</SignIn>
</div>
</Nav>
<Section>
<Hero>
<h1>Welcome to your professional community</h1>
<img src="/images/login-hero.svg" alt="" />
</Hero>
<Form>
<Google onClick={() => props.signIn()}>
<img src="/images/google.svg" alt="" />
Sign in with Google
</Google>
</Form>
</Section>
</Container>
);
};
const mapStateToProps = (state) => {
return {
user: state.userState.user,
};
};
const mapDispatchToProps = (dispatch) => ({
signIn: () => dispatch(signInAPI()),
});
export default connect(mapStateToProps, mapDispatchToProps)(Login);
//LogIn Section End to identify the error //
**SignIn**
import { auth, provider } from "../firebase";
import { signInWithPopup, onAuthStateChanged, signOut } from "firebase/auth";
import { SET_USER } from "./actionType";
export const setUser = (payload) => ({
type: SET_USER,
user: payload,
});
export function signInAPI() {
return (dispatch) => {
signInWithPopup(auth, provider)
.then((payload) => {
// console.log(payload.user);
dispatch(setUser(payload.user));
})
.catch((error) => alert(error.message));
};
}
export function getUserAuth() {
return (dispatch) => {
onAuthStateChanged(auth, (user) => {
console.log(user);
if (user) {
dispatch(setUser(user));
}
});
};
}
export function signOutAPI() {
return (dispatch) => {
signOut(auth)
.then(() => {
dispatch(setUser(null));
})
.catch((error) => {
console.log(error.message);
});
};
}
**End SignIn**
//**Throttling navigation** to prevent the browser from hanging.//
//Warning: Maximum update depth exceeded. This can happen when a component calls
setState inside useEffect, but useEffect either doesn't have a dependency array, or
one of the dependencies changes on every render. Unable to identify the error //
Related
I am following the WhatsApp clone on YouTube, I did exactly what they were doing but I don't know why I'm getting this error. I was
I read a lot of blogs, but I couldn't resolve it.
In app, it gives this error and couldn't dismiss.
./src/App.js Line 10: 'dispatch' is assigned a value but never used no-unused-vars
In login, it gives this error.
./src/Login.js Line 9: Unexpected empty object pattern no-empty-pattern
<!-- begin snippet: js hide: false console: true babel: false -->
import React from "react";
import "./Login.css";
import { Button } from "#mui/material";
import { auth, provider } from "./firebase";
import { useStateValue } from "./StateProvider";
import { actionTypes } from "./reducer";
function Login() {
const [value, dispatch] = useStateValue({});
// const [value, dispatch] = useStateValue({})
// const [{ type, user }, dispatch] = useStateValue();
const signIn = () => {
auth
.signInWithPopup(provider)
.then((result) => {
dispatch({
type: actionTypes.SET_USER,
user: result.user,
});
})
.catch((error) => alert(error.message));
};
return (
<div className="login">
<div className="login__container">
<img
src="https://www.freepnglogos.com/uploads/whatsapp-logo-png-hd-2.png"
alt=""
/>
<div className="login__text">
<h1>Sign in to WhatsApp</h1>
</div>
<Button onClick={signIn}>Sign In with Google</Button>
</div>
</div>
);
}
export default Login;
import React from "react";
import "./App.css";
import Sidebar from "./Sidebar";
import Chat from "./Chat";
import Login from "./Login";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { useStateValue } from "./StateProvider";
function App() {
const [{ user }, dispatch] = useStateValue();
return (
<div className="app">
{!user ? (
<Login />
) : (
<div className="app__body">
<Router>
<Sidebar />
<Routes>
<Route path="/rooms/:roomId" element={<Chat />} />
<Route path="/" element={<Chat />} />
</Routes>
</Router>
</div>
)}
</div>
);
}
export default App;
import React, { createContext, useContext, useReducer } from "react";
export const StateContext = createContext();
export const StateProvider = ({ reducer, initialState, children }) => (
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
);
export const useStateValue = () => useContext(StateContext);
export const initialState = {
user: null,
};
export const actionTypes = {
SET_USER: "SET_USER",
};
const reducer = (state, action) => {
console.log(action);
switch (action.type) {
case actionTypes.SET_USER:
return {
...state,
user: action.user,
};
default:
return state;
}
};
export default reducer;
I am making a site whereby after the user signs in, the user is meant to be redirected to the home page. The homepage and all the other pages of the site are only accessible by signed in users but even after a user signs in(firebase auth), the rest of the site(protected routes) is still not accessible and the only page accessible is the login page. The technologies I am using are react, react router dom and firebase and this is how my code looks like, starting with the App.js
import Home from "./pages/home/Home";
import Login from "./pages/login/Login";
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import List from "./pages/list/List";
import User from "./pages/user/User";
import AddNew from "./pages/addnew/AddNew";
import { useContext } from "react";
import { AuthContext } from "./context/AuthContext";
function App() {
const {currentUser} = useContext(AuthContext);
const RequireAuth = ({ children }) => {
return currentUser ? children : <Navigate to="/login" />;
};
return (
<div className="App">
<BrowserRouter>
<Routes>
<Route path="/login" exact element={<Login />} />
<Route path="/" exact element={ <RequireAuth> <Home /> </RequireAuth> } />
<Route path="/users" exact element={<RequireAuth><List /></RequireAuth>} />
<Route path="/users/:id" exact element={<RequireAuth><User /></RequireAuth>} />
<Route path="/add" exact element={<RequireAuth><AddNew /></RequireAuth>} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
And then followed by the login.js page
import React,{useState} from 'react'
import { useContext } from 'react';
import { signInWithEmailAndPassword } from "firebase/auth";
import { auth } from '../../firebase';
import "./login.css";
import {useNavigate} from "react-router-dom";
import { AuthContext } from '../../context/AuthContext';
export default function Login() {
const [error, seterror] = useState(false);
const [email, setemail] = useState("");
const [password, setpassword] = useState("");
const navigate = useNavigate();
const {dispatch} = useContext(AuthContext)
const handleLogin = (e) => {
e.preventDefault();
signInWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
const user = userCredential.user;
dispatch({type: "LOGIN", payload: user});
navigate("/");
})
.catch((error) => {
seterror(true);
console.log(error.message);
});
}
return (
<div className='login'>
<form onSubmit={handleLogin}>
<input className='ok' type="email" placeholder='email' onChange={e => setemail(e.target.value)} />
<input className='ok' type="password" placeholder='password' onChange={e => setpassword(e.target.value)} />
<button className='sb'>Submit</button>
{error && <span className='ks'>Wrong email or password</span>}
</form>
</div>
)
}
And then I have the authreducer.js file that deals with the state
const AuthReducer = (state, action) => {
switch (action.type) {
case "LOGIN": {
return {
currentUser: action.payload,
}
}
case "LOGOUT": {
return {
currentUser: null
}
}
default:
return state;
}
}
export default AuthReducer
And finally the authcontext.js file
import { createContext, useEffect, useReducer } from "react";
import AuthReducer from "./AuthReducer";
const INITIAL_STATE = {
currentUser: JSON.parse(localStorage.getItem("user")) || null,
}
export const AuthContext = createContext(INITIAL_STATE);
export const AuthContextProvider = ({children}) => {
const [state, dispatch] = useReducer(AuthReducer, INITIAL_STATE);
useEffect(() => {
localStorage.setItem("user", JSON.stringify(state.currentUser))
}, [state.currentUser])
return (
<AuthContext.Provider value={{current: state.current, dispatch}}>
{children}
</AuthContext.Provider>
)
}
I do not know what could be causing this problem but I have an idea that it has something to do with the state because it was redirecting well before I started combining it with the state. What could be the problem
Issue
From that I can see, the App isn't destructuring the correct context value to handle the conditional route protection.
The AuthContextProvider provides a context value with current and dispatch properties
<AuthContext.Provider value={{ current: state.current, dispatch }}>
{children}
</AuthContext.Provider>
but App is accessing a currentUser property, which is going to be undefined because state.current is undefined.
const { currentUser } = useContext(AuthContext);
const RequireAuth = ({ children }) => {
return currentUser ? children : <Navigate to="/login" />;
};
The Navigate component will always be rendered.
Solution
Assuming the handleLogin handler correctly updates the state then the solution is to be consistent with state properties.
<AuthContext.Provider value={{ currentUser: state.currentUser, dispatch }}>
{children}
</AuthContext.Provider>
...
const { currentUser } = useContext(AuthContext);
const RequireAuth = ({ children }) => {
return currentUser ? children : <Navigate to="/login" />;
};
I'm using React.js and firebase to build a web app. Using firebase, when the auth state changes as the user signs in with google, the pages is supposed to be redirected to the "/home" route. instead, it signs in and then "freezes". The page becomes unresponsive. I have checked the console logs on the localhost:3000 page. It keeps throwing a warning multiple times. see below
Kindly help me fix this.
Throttling navigation to prevent the browser from hanging. See https://crbug.com/1038223. Command line switch --disable-ipc-flooding-protection can be used to bypass the protection
below is my code.
//Header.js component
import { signInWithPopup } from "firebase/auth";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import { selectUserEmail, selectUserName, selectUserPhoto, setSignOutState, setUserLoginDetails } from "../features/user/userSlice";
import { auth, provider } from "../firebase";
//header component.
const Header = (props) => {
const dispatch = useDispatch();
const navigate = useNavigate();
const userEmail = useSelector(selectUserEmail);
const userName = useSelector(selectUserName);
const userPhoto = useSelector(selectUserPhoto);
// moniter auth state. if user signs in , redirect him to homepage
useEffect(() => {
auth.onAuthStateChanged(
async (user) => {
if (user) {
setUser(user);
navigate("/home");
}
},
[userName, userEmail, userPhoto]
);
});
//handle auth ftn
const handleAuth = async () => {
provider.addScope("profile");
provider.addScope("email");
if (!userName) {
const result = await signInWithPopup(auth, provider);
setUser(result.user).catch((error) => {
alert(error.message);
});
console.log(result);
} else if (userName) {
auth
.signOut()
.then(() => {
dispatch(setSignOutState());
navigate("/");
})
.catch((error) => {
alert(error.message);
});
}
};
//dispatch ftn
const setUser = (user) => {
dispatch(
setUserLoginDetails({
name: user.displayName,
email: user.email,
photo: user.photoURL,
})
);
};
//UI of the COMPONENT
return (
<Nav>
<Logo>
<img src="/images/logo.svg" alt="Disney Logo" />
</Logo>
{!userName ? (
<Login onClick={handleAuth}>LOGIN</Login>
) : (
<>
<NavMenu>
<a href="/home">
<img src="/images/home-icon.svg" alt="home" />
<span>HOME</span>
</a>
<a href="/search">
<img src="/images/search-icon.svg" alt="home" />
<span>SEARCH</span>
</a>
<a href="/watchlist">
<img src="/images/watchlist-icon.svg" alt="home" />
<span>WATCHLIST</span>
</a>
<a href="/original">
<img src="/images/original-icon.svg" alt="home" />
<span>ORIGINALS</span>
</a>
<a href="/movies">
<img src="/images/movie-icon.svg" alt="home" />
<span>MOVIES</span>
</a>
<a href="/series">
<img src="/images/series-icon.svg" alt="home" />
<span>SERIES</span>
</a>
</NavMenu>
<Signout>
<UserImg src={userPhoto} alt={userName} />
<DropDown>
<span onClick={handleAuth}>Sign out</span>
</DropDown>
</Signout>
</>
)}
</Nav>
);
};
//App.js code
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import "./App.css";
import Header from "./components/Header";
import Home from "./components/Home";
import Login from "./components/Login";
function App() {
return (
<div className="App">
<Router>
<Header />
<Routes>
<Route path="/" element={<Login />}></Route>
<Route path="/home" element={<Home />}></Route>
</Routes>
</Router>
</div>
);
}
export default App;
useEffect(() => {
auth.onAuthStateChanged(async (user) => {
if (user) {
setUser(user);
navigate("/home");
}
});
}, [userName]);
just added userName as a dependency and it fixed it. The problem was that without the dependency, it was throwing an infinite loop.
I am following the tutorial here:
https://github.com/auth0-blog/auth0-react-sample
I have most of the authentication working but get the following issue:
The first time I log in (after a cache clear) it asks me for gmail username. If I select the appropriate one, it logs me in. If I logout, and click login it never asks me again, it just uses the original one I entered.
If I enter an invalid gmail, it doesn't login but never asks again.
Either way, I suspect it's using the original response that it gets back (either authenticated or not).
Is there a way I can change it so that if I click the logout button (or it fails to login) it deletes the response.
I believe this post from 2018 is similar but I'm not sure how to modify it to today:
https://community.auth0.com/t/does-not-show-user-login-prompt-anymore-after-the-first-logging-in/16504
I'm new to react/auth0 but here are what I think to be the relevant files:
auth0-provider-with-history.js
import React from "react";
import { useHistory } from "react-router-dom";
import { Auth0Provider } from "#auth0/auth0-react";
const Auth0ProviderWithHistory = ({ children }) => {
const history = useHistory();
const domain = process.env.REACT_APP_AUTH0_DOMAIN;
const clientId = process.env.REACT_APP_AUTH0_CLIENT_ID;
const audience = process.env.REACT_APP_AUTH0_AUDIENCE;
const onRedirectCallback = (appState) => {
history.push(appState?.returnTo || window.location.pathname);
};
return (
<Auth0Provider
domain={domain}
clientId={clientId}
redirectUri={window.location.origin}
onRedirectCallback={onRedirectCallback}
audience={audience}
>
{children}
</Auth0Provider>
);
};
export default Auth0ProviderWithHistory;
protected-route.js
// src/auth/protected-route.js
import React from "react";
import { Route } from "react-router-dom";
import { withAuthenticationRequired } from "#auth0/auth0-react";
import { Loading } from "../components/index";
const ProtectedRoute = ({ component, ...args }) => (
<Route
component={withAuthenticationRequired(component, {
onRedirecting: () => <Loading />,
})}
{...args}
/>
);
export default ProtectedRoute;
login-button.js
import React from "react";
import { useAuth0 } from "#auth0/auth0-react";
const LoginButton = () => {
const { loginWithRedirect } = useAuth0();
return (
<button
className="btn btn-primary btn-block"
onClick={() => loginWithRedirect()}
>
Log In
</button>
);
};
export default LoginButton;
logout-button.js
import React from "react";
import { useAuth0 } from "#auth0/auth0-react";
const LogoutButton = () => {
const { logout } = useAuth0();
return (
<button
className="btn btn-danger btn-block"
onClick={() =>
logout({
returnTo: window.location.origin,
})
}
>
Log Out
</button>
);
};
export default LogoutButton;
I am not sure if this is the best way to do it, but I was able to get this working by adding localStorage.clear(); in appropriate locations to delete anything that was stored.
Hopefully this helps someone in the future.
Specifically:
logout-button.js
import React from "react";
import { useAuth0 } from "#auth0/auth0-react";
const LogoutButton = () => {
const { logout } = useAuth0();
localStorage.clear(); // this clears the old email address
return (
<button
className="btn btn-danger btn-block"
onClick={() =>{
logout({
returnTo: window.location.origin,
})
}
}
>
Log Out
</button>
);
};
export default LogoutButton;
app.js
import React from "react";
import { Route, Switch } from "react-router-dom";
import { useAuth0 } from "#auth0/auth0-react";
import { NavBar, Footer, Loading } from "./components";
import { Home, Profile, ExternalApi } from "./views";
import ProtectedRoute from "./auth/protected-route";
import "./app.css";
const App = () => {
const { isLoading, logout, error} = useAuth0();
if (isLoading) {
return <Loading />;
}
if (typeof error !== 'undefined' && error.error === "unauthorized") { // The user does not exist. Completely log them out.
localStorage.clear();
logout({
returnTo: window.location.origin,
});
}
return (
<div id="app" className="d-flex flex-column h-100">
<NavBar />
<div className="container flex-grow-1">
<Switch>
<Route path="/" exact component={Home} />
<ProtectedRoute path="/profile" component={Profile} />
<ProtectedRoute path="/external-api" component={ExternalApi} />
</Switch>
</div>
<Footer />
</div>
);
};
export default App;
I am making a simple SPA where you need to login before you can access other pages. I can successfully login and store the login data (firstname, lastname, etc.) cause I plan to use the data again later in the other pages. The problem is whenever I refresh the page, it always empty the state in the context which cause me to return to the login page. I am referring link for my SPA.
Do I need to do this? I would be thankful if someone can point out what I should change / improve. Thank you.
Here is my code.
App.js
import React, { useState } from "react";
import { BrowserRouter as Router, Link, Route } from "react-router-dom";
import { AuthContext } from "./context/auth";
import PrivateRoute from "./PrivateRoute";
import Login from "./pages/Login";
import Signup from "./pages/Signup";
import Home from "./pages/Home";
import Admin from "./pages/Admin";
function App() {
const [authTokens, setAuthTokens] = useState();
const setTokens = (data) => {
// console.log("DATA ",data);
localStorage.setItem("tokens", JSON.stringify(data));
setAuthTokens(data);
}
// console.log(authTokens);
return (
<AuthContext.Provider value={{ authTokens, setAuthTokens: setTokens }}>
<Router>
<div className="app">
<ul>
<li><Link to="/">Home Page</Link></li>
<li><Link to="/admin">Admin Page</Link></li>
</ul>
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={Signup} />
<Route exact path="/" component={Home} />
<PrivateRoute exact path="/admin" component={Admin} />
</div>
</Router>
</AuthContext.Provider>
);
}
export default App;
Login.js
import React, { useState } from "react";
import axios from "axios";
import { Link, Redirect } from "react-router-dom";
import { useAuth } from "../context/auth";
import { Card, Form, Input, Button, Error } from "../components/AuthForm";
const Login = () => {
const [isLoggedIn, setLoggedIn] = useState(false);
const [isError, setIsError] = useState(false);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const { setAuthTokens } = useAuth();
const handleLogin = () => {
axios
.post("LOGINLINK", {
email,
password,
})
.then((result) => {
if (result.status === 200) {
setAuthTokens(result.data);
setLoggedIn(true);
} else {
setIsError(true);
}
})
.catch((error) => {
setIsError(true);
});
};
if (isLoggedIn) {
return <Redirect to="/" />;
}
return (
<Card>
<Form>
<Input
type="email"
placeholder="Email"
value={email}
onChange={(e) => {
setEmail(e.target.value);
}}
/>
<Input
type="password"
placeholder="password"
value={password}
onChange={(e) => {
setPassword(e.target.value);
}}
/>
<Button onClick={handleLogin}>Login</Button>
</Form>
<Link to="/signup">Don't have an account?</Link>
{isError && (
<Error>The username or password provided were incorrect!</Error>
)}
</Card>
);
};
export default Login;
Auth.js
import { createContext, useContext } from "react";
export const AuthContext = createContext();
export function useAuth() {
console.log("CONTEXT", useContext(AuthContext));
return useContext(AuthContext);
}
In your App component you need to fetch the data from localStorage when initializing your state so it has some data to start with.
const localToken = JSON.parse(localStorage.getItem("tokens"));
const [authTokens, setAuthTokens] = useState(localToken);
If user has already authenticated it will be available in localStorage else it's going to be null.
I also had same problem but I solved liked this Don't use localStorage directly use your state and if it is undefined then only use localStorage. cause directly manipulating state with localStorage is in contrast with react internal state and effects re-render .
const getToken = () => {
JSON.parse(localStorage.getItem('yourtoken') || '')
}
const setToken = (token) => {
localStorage.setItem('key' , token)
}
const [authTokens, setAuthTokens] = useState(getToken());
const setTokens = (data) => {
// console.log("DATA ",data);
setToken(token);
setAuthTokens(data);
}