Sign out and always pop up asking for account google firebase - javascript

I'm working with React and firebase, I'm capable of log in into the app with Google, and sign out as well, but if I press login again instead of asking me for an account, logged me with my previous user.
What I want is: when I sign out and want to log me again, the popup ask me for an account.
I've try almost all solution from internet, the most I tried is:
firebase.auth().signOut(), firebase.getInstance.auth().signOut(), and when I want to log again, the app log me right away, I want to ask for an account, I cant login without any other account.
import React from 'react';
import './App.scss';
import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom';
import Navbar from './components/Navigation/'
import Home from './components/Home'
import MiCuenta from './components/MiCuenta'
/* import ListUsers from './components/ListUsers' */
import MyAccount from './components/MyAccount'
import SignIn from './components/SignIn'
import withFirebaseAuth from 'react-with-firebase-auth'
import * as firebase from 'firebase';
import 'firebase/auth';
import firebaseConfig from './components/FirebaseConfig/';
const firebaseApp = firebase.initializeApp(firebaseConfig);
const firebaseAppAuth = firebaseApp.auth(firebaseApp);
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
user: {
username: '',
edad: Number
},
}
this.logout = () => firebase.auth().signOut()
.then(function() {
})
.catch(function(error) {
});
this.loadData = this.loadData.bind(this);
}
loadData(){
if(this.props.user){
console.log(this.props.user.uid)
let userRef = firebase.database().ref(`users`)
let currentUser = userRef.child(this.props.user.uid);
currentUser.on('value', data => {
if (data.exists()) {
this.setState({ user: data.val() })
console.log(this.state.user)
}
else {
firebase.database().ref('users/' + this.props.user.uid).set({
username: this.props.user.displayName,
nombre: 'string',
edad: 0,
hobbies: ['futbol', 'tenis']
});
}
})
} else{
console.log('not user')
}
}
componentDidUpdate(prevProps) {
if (this.props.user !== prevProps.user) {
this.loadData()
}
}
render() {
const {
user,
signOut ,
signInWithGoogle,
} = this.props;
return (
<div className="App">
{
user
?
<div>
<p>Hello, {this.state.user.username}</p>
<p>{this.state.user.edad}</p>
</div>
: <p>Please sign in. </p>
}
{
user
?
<React.Fragment>
<Router>
<div>
<button onClick={this.loadData}>loadData</button>
<Navbar signOut={this.logout} />
<Switch>
<Route path="/login" component={SignIn} />
<Route path="/Home" component={Home} />
<Route path="/MiCuenta" component={MiCuenta} />
<Route path="/MyAccount" component={() => <MyAccount userObject={this.state.user} />} />
{/* <Route exact path={"/ListUsers"} component={() => <ListUsers uid={user.uid} displayName={user.displayName} /> } /> */}
</Switch>
</div>
</Router>
</React.Fragment>
:
<div>
<button onClick={signInWithGoogle}>Sign in with Google</button>
</div>
}
</div>
);
}
}
const providers = {
googleProvider: new firebase.auth.GoogleAuthProvider(),
};
// high order component
export default withFirebaseAuth({
providers,
firebaseAppAuth,
})(App);

setCustomParameters works for me
my version: "firebase": "^9.6.1",
function googleSignIn() {
const provider = new GoogleAuthProvider();
// select account
provider.setCustomParameters({
prompt: "select_account",
});
return signInWithPopup(auth, provider);
}

Related

useContext() returns null value

I am trying to use useContext to get the current logged in user details. I am getting an error in my console which says:
Uncaught TypeError: user is null
children Animate.jsx:79
Animate Animate.jsx:76
I am getting this error whenever I Logout. I am facing no errors in the console when I am logged in.
Here is the code:
Context.js File:
import { onAuthStateChanged } from "firebase/auth";
import { useEffect, useState } from "react";
import { createContext } from "react";
import { auth } from "./firebase";
export const Context = createContext()
const ContextProviderfunc=({children})=>{
const use={
"mail":'history'
}
const [user,setuser]=useState({});
useEffect(()=>{
const unsubscribe = onAuthStateChanged(auth,(currentUser)=>{
// console.log(currentUser);
setuser(currentUser);
})
return ()=>{
unsubscribe();
}
},[])
return(
<Context.Provider value={user}>
{children}
</Context.Provider>
)
}
export default ContextProviderfunc
Animate.jsx File:
import React from 'react'
import {
BrowserRouter as Router, Routes, Route, useLocation, Link
} from 'react-router-dom'
import Navbar from './Navbar'
import RecipeCard from './RecipeCard'
import Details from './Details'
import { AnimatePresence, motion } from "framer-motion"
import Add from './Add'
import clicked from './RecipeCard'
import { useState } from 'react'
import Login from './Login'
import Register from './Register'
import Protected from './Protected'
import Logout from './Logout'
import Myrecipe from './Myrecipe'
import { useContext } from 'react'
import { Context } from '../Context'
export default function Animate(props) {
const user=useContext(Context)
const [pname, setpname] = useState()
function assign(nigga) {
setpname(nigga);
console.log(nigga);
}
const location = useLocation()
return (
<motion.div className='all'
>
<AnimatePresence>
<Routes location={location} key={location.pathname}>
<Route path='/Login' element={<Login />} />
<Route path='/Register' element={<Register />} />
<Route path='*' element={[<Protected><Navbar /></Protected>, <h2>Recipes</h2>,
<div className='recipes'>
{props.query ? props.query.map((object, i) => (
<Link className='lin' to='/Details'>
<div onClick={() => assign(object.Name)}>
<Protected><RecipeCard
src={object.src}
name={object.Name}
ingredients={object.Ingredients}
steps={object.Steps}
key={i}
/></Protected>
</div></Link>
)) : "Loading"}
<Link className='lin' to='/Add'>
<div className='cardcont'>
<img src='https://cdn.pixabay.com/photo/2017/11/10/05/24/add-2935429_960_720.png' alt="1" className='cardimg' />
<div className='cardbody'>
<h3>Add</h3>
</div>
</div></Link>
</div>
]} />
<Route path='/MyRecipe' element={[<Protected><Navbar /></Protected>, <h2>My Recipes</h2>,
<Protected>
<div className='recipes'>
{props.query2 ? props.query2.map((object, i) => {
if(user.email==object.email){
console.log(user.email);
return(<Link className='lin' to='/Details'>
<div onClick={() => assign(object.Name)}>
<Protected><RecipeCard
src={object.src}
name={object.Name}
ingredients={object.Ingredients}
steps={object.Steps}
key={i}
/></Protected>
</div></Link>)
}
}
) : "Loading"}
<Link className='lin' to='/Add'>
<div className='cardcont'>
<img src='https://cdn.pixabay.com/photo/2017/11/10/05/24/add-2935429_960_720.png' alt="1" className='cardimg' />
<div className='cardbody'>
<h3>Add</h3>
</div>
</div></Link>
</div>
</Protected>
]} />
<Route path='/Details' element={
[<Navbar />, props.query ? props.query.map((object, m) => {
if (pname == object.Name) {
console.log(object.Name);
return (<Details
src={object.src}
name={object.Name}
Ingredients={object.Ingredients}
Steps={object.Steps}
key={m}
/>)
}
}) : "Loading"]} />
<Route path='/Add' element={[<Navbar />, <Add />]} />
</Routes>
</AnimatePresence>
</motion.div>
)
}
line 79 of Animate.jsx
if(user.email==object.email){
console.log(user.email);
App.js file:
import Navbar from './components/Navbar';
import RecipeCard from './components/RecipeCard';
import { useContext, useEffect, useState } from 'react';
import { auth, db, storage } from './firebase'
import { collection, addDoc, getDocs, doc } from "firebase/firestore";
import { async } from '#firebase/util';
import Details from './components/Details'
import Animate from './components/Animate';
import {BrowserRouter as Router, Route,Routes} from 'react-router-dom'
import Add from './components/Add';
import ContextProviderfunc from './Context';
import { Context } from './Context';
function App() {
const [query, setquery] = useState()
const [query2, setquery2] = useState()
const [recipe, setrecipe] = useState()
const add = async (e) => {
e.preventDefault()
try {
const docRef= await addDoc(collection(db, "recipe"),
{
src: 'https://images.immediate.co.uk/production/volatile/sites/30/2017/02/Two-panna-cotta-on-plates-298e616.jpg',
ingredients: ['Dish'],
steps: ['Description']
}, {
src: 'https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F43%2F2022%2F04%2F19%2F22749-the-best-banana-pudding-mfs-366-1x1-1.jpg',
ingredients: ['Dish'],
steps: ['Description']
});
// console.log('morein');
// console.log("Document written with ID: ", docRef.id);
} catch (error) {
console.log('morein');
console.error("Error adding document: ", error);
}
}
useEffect(() => {
const getrecipe = async () => {
const data = await getDocs(collection(db, 'recipe'))
const data2 = await getDocs(collection(db, 'userrecipe'))
console.log(data);
setquery(data.docs.map((doc) => (
{
...doc.data(), id: doc.id
})
))
setquery2(data2.docs.map((doc) => (
{
...doc.data(), id: doc.id
})
))
console.log(query);
};
getrecipe();
}, []);
useEffect(() => {
// searchRecipes();
}, [])
return (
<div>
<ContextProviderfunc>
<Router>
<Animate query={query} query2={query2}/>
</Router>
</ContextProviderfunc>
</div>
);
}
export default App;
These two files I think are needed to debug. If any more code is needed I will provide it. Please find out the issue in the above code
Whenever the user logs out, value of currentUser will become null in here:
onAuthStateChanged(auth,(currentUser)=>{
// console.log(currentUser);
setuser(currentUser); // currentUser is null
})
Since you are updating the state (setuser), The Context Provider rerenders and passes down null as value.
return(
<Context.Provider value={user}> // user is null, and is being passed down
{children}
</Context.Provider>
)
Within Animate you receive the null value.
const user=useContext(Context) // user is null, after loging out.
Long story short, user is at times null within Animate, so you don't have the guarantee to use properties like email, like this line below:
if(user.email==object.email){
console.log(user.email);
The solution: Wherever you need to use user, first check if it equals to null.
If you set logged out user ti be null, you should not expect to retrieve email address of null.
You should check (read from context) if user still logged in.
For example :
if(user) {
return <div>I am logged in user and my email is {user.email} </div>
}
return <div>You are not logged In </div>
Specific to your case: check first user and then user.email to avoid Uncaught TypeError: user is null
if(user && user.email === object.email) .....
This is just a friendly suggestion, which is not related with your question: Use ===instead of == for for information you can read this page

Why the logout button is not navigating to login page?

I am learning firebase. I have a button log out. When I am clicking on the button it is not navigating to login page.
I have set routing with user.emailVerified value.
Nav.jsx
import { getAuth, signOut } from 'firebase/auth';
import React from 'react';
import { useDispatch } from 'react-redux';
import { Link, useNavigate } from 'react-router-dom';
import { setLogOut } from '../redux/slice';
const Nav = () => {
const navigate = useNavigate();
const auth = getAuth();
const dispatch = useDispatch()
const logOutHandler = () => {
signOut(auth)
.then(() => {
dispatch(setLogOut())
navigate('/login', { replace: true });
})
.catch(error => {
console.log(error);
});
};
return (
<nav className="navbar navbar-expand-lg bg-light ">
<div className="container">
<Link className="navbar-brand" to="/">
Navbar
</Link>
<Link className="nav-link" to="/">
Home
</Link>
<button className="btn btn-outline-secondary" onClick={logOutHandler}>
Log Out
</button>
</div>
</nav>
);
};
export default Nav;
App.jsx
import React, { useState } from 'react';
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/js/bootstrap.min.js';
import 'bootstrap-icons/font/bootstrap-icons.css';
import { Routes, Route, Navigate } from 'react-router-dom';
import Nav from './components/Nav';
import Home from './pages/Home';
import Login from './pages/Login';
import SignUp from './pages/SignUp';
import EmailVerify from './pages/EmailVerify';
import { getAuth, onAuthStateChanged } from 'firebase/auth';
const App = () => {
const [verified, setverified] = useState();
// get user information
const auth = getAuth();
onAuthStateChanged(auth, user => {
if (user) {
setverified(user.emailVerified);
} else {
// User is signed out
// ...
}
});
let routes;
if (verified) {
routes = (
<>
<Nav />
<Routes>
<Route path="/" element={<Home />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</>
);
} else {
routes = (
<>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<SignUp />} />
<Route path="/email-verify" element={<EmailVerify />} />
<Route path="*" element={<Navigate to="/login" replace />} />
</Routes>
</>
);
}
return <>{routes}</>;
};
export default App;
redux
import { createSlice } from '#reduxjs/toolkit';
import { getAuth, onAuthStateChanged } from 'firebase/auth';
const initialState = {
email: JSON.parse(localStorage.getItem('email')),
isLoggedIn: JSON.parse(localStorage.getItem('loggedInStatus')),
};
export const slice = createSlice({
name: 'slice',
initialState,
reducers: {
setLogInValue: (state, action) => {
localStorage.setItem('email', JSON.stringify(action.payload));
localStorage.setItem('loggedInStatus', JSON.stringify(true));
state.email = action.payload;
state.isLoggedIn = true;
},
setLogOut: state => {
localStorage.setItem('email', null);
localStorage.setItem('loggedInStatus', false);
state.email = null;
state.isLoggedIn = false;
},
},
});
export const { setLogInValue, setLogOut } = slice.actions;
export default slice.reducer;
I am wanting to go login page when I click on log out button because when I am clicking on logout it is user is null then. But why not navigating to login page? How can I do that?
I have added setverified(false) on else condition of the onAuthStateChanged on App.jsx and it is working as I wanted.
Updated portion:
onAuthStateChanged(auth, user => {
if (user) {
setverified(user.emailVerified);
} else {
setverified(false); // updated line
}
});

How can I redirect a user to login when a certain link is clicked using JWT

I am trying to make it so that when a user clicks on a certain Link (Upload Component), it will redirect them to to the login component and am not sure how to accomplish this task in React. I was directed to another answered question, but it hasn't helped me as I am still confused on what I need to do with my own set up. I understand I need to make my own protected route (maybe), but I saw others accessing useContext and I do not have any file with context. I am using Version 6 in react dom. I am also using React router and redux in my project so I know I need to access the state somehow, just not sure how to wrap my mind around it so if anyone could help, I would appreciate it. The user is being stored in local storage with JWT authentication.
App.js:
import React, {useState, useEffect, useCallback} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {BrowserRouter as Router, Routes, Route} from 'react-router-dom'; //Switch was replaced by Routes in react-router-dom v6
import './App.css';
import Navbar from './components/Navbar';
import Footer from './components/Footer';
import Home from './components/pages/Home';
import Uploads from './components/pages/Uploads';
import Account from './components/pages/Account';
import Login from './components/pages/Login';
import LogOut from './components/Logout';
import Register from './components/pages/Register';
import Profile from './components/pages/Profile';
import EditProfile from './components/pages/EditProfile';
import BoardAdmin from './components/pages/BoardAdmin';
import BoardModerator from './components/pages/BoardModerator';
import BoardUser from './components/pages/BoardUser';
import {logout} from './slices/auth';
import EventBus from './common/EventBus';
const App = () => {
const [showModeratorBoard, setShowModeratorBoard] = useState(false);
const [showAdminBoard, setShowAdminBoard] = useState(false);
const {user: currentUser} = useSelector((state) => state.auth);
const dispatch = useDispatch();
useEffect(() => {
if (currentUser) {
setShowModeratorBoard(currentUser.roles.includes('ROLE_MODERATOR'));
setShowAdminBoard(currentUser.roles.includes('ROLE_ADMIN'));
} else {
setShowModeratorBoard(false);
setShowAdminBoard(false);
}
}, [currentUser]);
return (
<>
<Router>
<Navbar />
<Routes>
<Route path='/' element={<Home />} />
<Route path='/create/upload' element={<Uploads />} />
<Route path='/my-account' element={<Account />} />
<Route path='/login' element={<Login />} />
<Route path='/logout' element={<LogOut />} />
<Route path='/register' element={<Register />} />
<Route path='/profile' element={<Profile />} />
<Route path='/profile/edit' element={<EditProfile />} />
<Route path='/user' element={<BoardUser />} />
<Route path='/mod' element={<BoardModerator />} />
<Route path='/admin' element={<BoardAdmin />} />
</Routes>
<Footer />
</Router>
</>
);
};
export default App;
Auth.js:
import {createSlice, createAsyncThunk} from '#reduxjs/toolkit';
import {setMessage} from './messages';
import AuthService from '../services/auth.service';
const user = JSON.parse(localStorage.getItem('user'));
export const register = createAsyncThunk(
'auth/register',
async ({username, email, password}, thunkAPI) => {
try {
const response = await AuthService.register(username, email, password);
thunkAPI.dispatch(setMessage(response.data.message));
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
}
);
export const login = createAsyncThunk(
'auth/login',
async ({username, password}, thunkAPI) => {
try {
const data = await AuthService.login(username, password);
return {user: data};
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
}
);
export const logout = createAsyncThunk('auth/logout', async () => {
await AuthService.logout();
});
const initialState = user
? {isLoggedIn: true, user}
: {isLoggedIn: false, user: null};
const authSlice = createSlice({
name: 'auth',
initialState,
extraReducers: {
[register.fulfilled]: (state, action) => {
state.isLoggedIn = false;
},
[register.rejected]: (state, action) => {
state.isLoggedIn = false;
},
[login.fulfilled]: (state, action) => {
state.isLoggedIn = true;
state.user = action.payload.user;
},
[login.rejected]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
[logout.fulfilled]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
},
});
const {reducer} = authSlice;
export default reducer;
It looks like adding the if statement in my upload file is making it so that a logged in user can access the h1 element that is in the file, but not an unauthenticated user. So its working to an extent, just not redirecting back to login.
Upload.jsx:
import React from 'react';
import {Link} from 'react-router-dom';
import {useSelector} from 'react-redux';
function Uploads() {
const {user: currentUser} = useSelector((state) => state.auth);
if (!currentUser) {
return <Link to='/login' />;
}
return (
<>
<h1 className='page'>UPLOADS</h1>
</>
);
}
export default Uploads;
Rendering the Link doesn't imperatively navigate, use the Navigation component.
Example:
import React, { useEffect } from 'react';
import { Navigate } from 'react-router-dom';
import { useSelector } from 'react-redux';
function Uploads() {
const { user: currentUser } = useSelector((state) => state.auth);
if (!currentUser) {
return <Navigate to='/login' replace />;
}
return (
<>
<h1 className='page'>UPLOADS</h1>
</>
);
}
export default Uploads;
Typically you would protect the route instead. If you wanted to do that:
import { Navigate, Outlet } from 'react-router-dom';
import { useSelector } from 'react-redux';
const PrivateRoutes = () => {
const { user: currentUser } = useSelector((state) => state.auth);
return currentUser
? <Outlet />
: <Navigate to="/login" replace />;
}
...
<Routes>
<Route path='/' element={<Home />} />
<Route element={<PrivateRoutes />}>
<Route path='/create/upload' element={<Uploads />} />
... any other protected routes ...
</Route>
... other unprotected routes ...
</Routes>

How to create a protected route after login in reactjs

I'm quite new in react. I'm trying to create a project where users would have to login to access a page and I want to protect one route that should be accessible only after succesful login. I don't want fancy user management or anything like that so please don't recommend context or redux. I just want my localhost:3000/editor to be accessible only after login and if someone tries to access /editor without login then they are redirected to login page. so something like isAuthenicated: true/false would be fine in my case I believe but I'm not sure how to pass that in my webapp. I would highly if someone can show me how to do that?
I created this but many errors
App.js
import React, {useState} from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import "./App.css";
import Login from "./login";
import Dashboard from "./dashboard";
function App() {
const [loggedIN, setLoggedIN]=useState(false);
let privateRoutes = null;
if(loggedIN){
privateRoutes =(
<Route path="/dashboard" component={Dashboard} />
)}
return (
<>
<Router>
<div className="container">
<nav className="navbar navbar-expand-lg navheader">
<div className="collapse navbar-collapse">
<ul className="navbar-nav mr-auto">
<li className="nav-item">
<Link to={"/Login"} className="nav-link">
Login
</Link>
</li>
</ul>
</div>
</nav>
<br />
<Switch>
<Route exact path="/login" component={Login} />
{privateRoutes}
</Switch>
</div>
</Router>
</>);
}
export default App;
login.js
import React, { Component } from "react";
import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
import AppBar from "material-ui/AppBar";
import RaisedButton from "material-ui/RaisedButton";
import TextField from "material-ui/TextField";
import { Link } from "react-router-dom";
import "./loginForm.css";
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: ""
};
this.onchange = this.onchange.bind(this);
}
onchange(e) {
this.setState({ [e.target.name]: e.target.value });
}
performLogin = async () => {
var body = {
password: this.state.password,
email: this.state.email
};
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: JSON.stringify(body)
};
const url = "/api/authenticate";
try {
const response = await fetch(url, options);
const text = await response.text();
if (text === "redirect") {
this.props.setState({loggedIN: true})
this.props.history.push(`/dashboard`);
} else {
console.log("login failed");
window.alert("login failed");
}
} catch (error) {
console.error(error);
}
};
render() {
return (
<>
<div className="loginForm">
<MuiThemeProvider>
<TextField
hintText="Enter your Email"
floatingLabelText="Email"
onChange={(event, newValue) => this.setState({ email: newValue })}
/>
<br />
<TextField
type="password"
hintText="Enter your password"
floatingLabelText="password"
onChange={(event, newValue) =>
this.setState({ password: newValue })
}
/>
<br />
<RaisedButton
label="Submit"
primary={true}
style={style}
onClick={event => this.performLogin(event)}
/>
</MuiThemeProvider>
</div>
</>
);
}
}
const style = {
margin: 15
};
export default Login;
editor page
import React, { Component } from "react";
class Dashboard extends Component {
constructor(props) {
super(props);
}
logout=()=>{
this.setState({loggedIN: false)}
}
render() {
return (
<div>hello</div>;
<button onCLick={logout}>Logout</button>
)
}
}
export default Dashboard;
edit: codesandbox
After successfully logged in, maybe you will get token for authorization purpose.
so after successfully logged in you can store the auth token in cookies.
install - npm i universal-cookie --save
login.js
import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
import AppBar from "material-ui/AppBar";
import RaisedButton from "material-ui/RaisedButton";
import TextField from "material-ui/TextField";
import { Link } from "react-router-dom";
import "./loginForm.css";
import Cookies from 'universal-cookie';
const cookies = new Cookies();
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: ""
};
this.onchange = this.onchange.bind(this);
}
onchange(e) {
this.setState({ [e.target.name]: e.target.value });
}
performLogin = async () => {
var body = {
password: this.state.password,
email: this.state.email
};
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: JSON.stringify(body)
};
const url = "/api/authenticate";
try {
const response = await fetch(url, options);
const text = await response.text();
// here you will get auth token in response
// set token in cookie like cookie.set('token', response.token);
cookies.set('loggedin', true);
if (text === "redirect") {
this.props.setState({loggedIN: true})
this.props.history.push(`/dashboard`);
} else {
console.log("login failed");
window.alert("login failed");
}
} catch (error) {
console.error(error);
}
};
render() {
return (
<>
<div className="loginForm">
<MuiThemeProvider>
<TextField
hintText="Enter your Email"
floatingLabelText="Email"
onChange={(event, newValue) => this.setState({ email: newValue })}
/>
<br />
<TextField
type="password"
hintText="Enter your password"
floatingLabelText="password"
onChange={(event, newValue) =>
this.setState({ password: newValue })
}
/>
<br />
<RaisedButton
label="Submit"
primary={true}
style={style}
onClick={event => this.performLogin(event)}
/>
</MuiThemeProvider>
</div>
</>
);
}
}
const style = {
margin: 15
};
export default Login;
After that in the dashboard component check for the loggedin boolean in a cookie if it exists then its logged in and authenticated. eg.
dashboard.js
import React, { Component } from "react";
import {Redirect} from 'react-router-dom'
import Cookies from 'universal-cookie';
const cookies = new Cookies();
class Dashboard extends Component {
constructor(props) {
super(props);
}
logout=()=>{
this.setState({loggedIN: false)}
cookies.set('loggedin', false);
}
render() {
// notice here
if (!cookies.get('loggedin')) {
return (<Redirect to={'/login'}/>)
}
return (
<div>hello</div>;
<button onCLick={logout}>Logout</button>
)
}
}
export default Dashboard;
I think you have error here or it is better to change it
let privateRoutes = null;
if(loggedIN){
privateRoutes =(
<Route path="/dashboard" component={Dashboard} />
)}
change it to
let privateRoutes =()=>{
if(loggedIN){
return(
<Route path="/dashboard" component={Dashboard} />
)
}
}
but you don't need this!
you could use render route
<Route path="/home" render={() => {
if(loggedIN){
return(<Dashboard ...props/>)
}else{
return(<Login ...props/>)
}
}} />
note that after login ((loggedIN)) will be true in App.js and it will go to Dashboard automatically
ref:https://reacttraining.com/react-router/web/api/Route/render-func
here you have Big Bugs:
import React, { Component } from "react";
class Dashboard extends Component {
constructor(props) {
super(props);
}
// logout function should not be here it should write in app.js and pass to
// this component with props and just call here if needed
// and you should use this.logout = () ...
logout=()=>{
// and here
this.setState({loggedIN: false**)**}
}
render() {
// here!!! jsx element should wrapped with an element
return (
// <div>
<div>hello</div>;
<button onCLick={logout}>Logout</button>
//</div>
)
}
}
export default Dashboard;
The most efficient way is to split routes and manage redirect with an HOC like [redux-auth-wrapper][2]
App.js
import { createDevTools } from 'redux-devtools'
import LogMonitor from 'redux-devtools-log-monitor'
import DockMonitor from 'redux-devtools-dock-monitor'
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import { Provider } from 'react-redux'
import thunkMiddleware from 'redux-thunk'
import * as reducers from './reducers'
import App from './components/App'
const reducer = combineReducers(Object.assign({}, reducers, {}))
const DevTools = createDevTools(
<DockMonitor toggleVisibilityKey="ctrl-h"
changePositionKey="ctrl-q">
<LogMonitor theme="tomorrow" />
</DockMonitor>
)
const enhancer = compose(
// Middleware you want to use in development:
applyMiddleware(thunkMiddleware),
DevTools.instrument()
)
// Note: passing enhancer as the last argument requires redux#>=3.1.0
const store = createStore(reducer, enhancer)
ReactDOM.render(
<Provider store={store}>
<div>
<App />
{/* <DevTools /> */}
</div>
</Provider>,
document.getElementById('mount')
)
auth.js
import locationHelperBuilder from 'redux-auth-wrapper/history4/locationHelper'
import { connectedRouterRedirect } from 'redux-auth-wrapper/history4/redirect'
import connectedAuthWrapper from 'redux-auth-wrapper/connectedAuthWrapper'
import Loading from './components/Loading'
const locationHelper = locationHelperBuilder({})
const userIsAuthenticatedDefaults = {
authenticatedSelector: state => state.user.data !== null,
authenticatingSelector: state => state.user.isLoading,
wrapperDisplayName: 'UserIsAuthenticated'
}
export const userIsAuthenticated = connectedAuthWrapper(userIsAuthenticatedDefaults)
export const userIsAuthenticatedRedir = connectedRouterRedirect({
...userIsAuthenticatedDefaults,
AuthenticatingComponent: Loading,
redirectPath: '/login'
})
export const userIsAdminRedir = connectedRouterRedirect({
redirectPath: '/',
allowRedirectBack: false,
authenticatedSelector: state => state.user.data !== null && state.user.data.isAdmin,
predicate: user => user.isAdmin,
wrapperDisplayName: 'UserIsAdmin'
})
const userIsNotAuthenticatedDefaults = {
// Want to redirect the user when they are done loading and authenticated
authenticatedSelector: state => state.user.data === null && state.user.isLoading === false,
wrapperDisplayName: 'UserIsNotAuthenticated'
}
export const userIsNotAuthenticated = connectedAuthWrapper(userIsNotAuthenticatedDefaults)
export const userIsNotAuthenticatedRedir = connectedRouterRedirect({
...userIsNotAuthenticatedDefaults,
redirectPath: (state, ownProps) => locationHelper.getRedirectQueryParam(ownProps) || '/protected',
allowRedirectBack: false
})
Working example based on React Router v4
What I do
in main routing (usualli App.js):
import { userIsAuthenticated, userIsNotAuthenticated } from './auth';
// ...code
const isNotAuth = userIsNotAuthenticated(Login);
const isAuth = userIsAuthenticated(AuthRoutes);
// ...code
<Switch>
<Route exact path="/login" component={isNotAuth} /> // Notice the *exact* path
<Route path="/" component={isAuth} /> // Notice the *not exact path*
<Route path="" component={NotFoundPage} />
</Switch>
AuthRoutes.js
Those routes will be provided only if the user is authenticated
// ...code
<Switch>
<Route exact path="/homepage" component={HomePage} />
// ...other routes
</Switch>
[2] https://github.com/mjrussell/redux-auth-wrapper

How To Redirect to Login Page Before Entering Index Route in ReactJS + REDUX [duplicate]

This question already has answers here:
What component lifecycle to use to do something before render()?
(4 answers)
Closed 3 years ago.
I am really new to react, now i want to implement a login page before entering to any routes, i am currently using :
React : 15.4.2,
React-DOM : 15.4.2,
React-Redux : 5.0.4,
and i am using webpack and es2015 as my preset.
i have done so much googling, but i still does not found any solution, here is current source code :
for index.js :
import React from "react";
import ReacrDOM from "react-dom";
import {Router, Route, IndexRoute, hashHistory} from "react-router"
import {Provider} from "react-redux";
import store from "./store";
import Layout from "./pages/Layout";
import Dashboard from "./pages/Dashboard";
import Login from "./pages/login/index";
import User from "./pages/user/index";
import EnsureLoggedIn from "./EnsureLoggedIn";
const app = document.getElementById("app");
ReacrDOM.render(
<Provider store={store}>
<Router history={hashHistory}>
<Route path="/login" component={Login}/>
<Route component={EnsureLoggedIn}>
<Route path="/" component={Layout}>
<IndexRoute component={Dashboard}/>
<Route path="user" component={User}/>
</Route>
</Route>
</Router>
</Provider>
, app);
Here is my Dashboar Component :
import React from "react";
import {connect} from "react-redux";
#connect((store) => {
return {
}
})
export default class Dashboard extends React.Component {
componentWillMount() {
}
render() {
return (
<div>
<h3>Dashboard</h3>
<hr/>
</div>
);
}
}
and here is my EnsureLoggedIn File :
import React from "react";
import {browserHistory} from 'react-router';
import {connect} from "react-redux";
import {login_check, show_log} from "./actions/logActions";
#connect((store) => {
return {
session_key: store.logReducer.session_key,
user_id: store.logReducer.user_id,
error_message: store.logReducer.error_message,
logged_in: store.logReducer.logged_in
}
})
export default class EnsureLoggedIn extends React.Component {
componentDidMount() {
if (!this.isLoggedIn()) {
browserHistory.push('#/login');
}
}
isLoggedIn() {
this.props.dispatch(show_log());
var account = {user_id: this.props.user_id, session_key: this.props.session_key};
this.props.dispatch(login_check(account));
if (!this.props.logged_in) {
return false;
}
return true;
}
render() {
return this.props.children
}
}
and here is my logReducer :
export default function (state = {
user_id: null,
session_key: null,
error_message: null,
logged_in: false,
log_error: null,
}, action) {
switch (action.type) {
case "LOGIN": {
var {id, session_key}=action.payload.data.data
state = {
...state,
user_id: id,
session_key,
error_message: null,
logged_in: true,
log_error: null
};
break;
}
case "LOGIN_FAILED": {
state = {
...state,
error_message: null,
log_error: action.payload.response.data.error.message,
logged_in: false
};
break;
}
case "CHECK_LOGIN": {
state = {...state, error_message: null, logged_in: true};
break;
}
case "CHECK_LOGIN_FAILED": {
state = {
...state,
error_message: action.payload.response.data.error.message,
log_error: null,
logged_in: false
};
break;
}
case "LOGOUT": {
state = {
...state,
user_id: null,
session_key: null,
error_message: null,
log_error: null,
logged_in: false
};
break;
}
case "GET_LOG_DATA": {
state = {...state};
break;
}
}
return state;
}
and this is my logActions file :
import axios from "axios";
const basicAuth = {
headers: {"authorization": "Basic dmlkeTpwdmlkeQ=="}
}
export function login(email = "", password = "") {
var url = 'http://localhost:8080/dfordatabase/api/api/login';
return function (dispatch) {
axios.post(url, {email: email, password: password}, basicAuth)
.then((response) => {
dispatch({type: "LOGIN", payload: response});
}).catch((error) => {
dispatch({type: "LOGIN_FAILED", payload: error});
})
}
}
export function login_check(account = {}) {
var url = 'http://localhost:8080/dfordatabase/api/api/login_check';
return function (dispatch) {
axios.post(url, {...account}, basicAuth)
.then((response) => {
dispatch({type: "CHECK_LOGIN", payload: response});
}).catch((error) => {
dispatch({type: "CHECK_LOGIN_FAILED", payload: error});
})
}
}
export function show_log() {
return function (dispatch) {
dispatch({type: "GET_LOG_DATA", payload: null});
}
}
the problem is : indeed login page will appear at first when i run the code, but when i fill the credential and logged in, page is not redirect to my index route which is "/", the url has change, but dashboard won't appear, if i refresh the page dashboard appear but, the problem is all state of my log reducer become empty again.
can anyone tell me where can i find some example of similar development tutorial. Please kindly help me..
The property onEnter has been removed since react-router-4 , now you should use the render method instead, here's an example how to do a redirect :
<Route exact path="/home" render={(props) => (
isUserLoggedIn() ? (
<Home {...props} />
) : (
<Redirect to="/login"/>
)
)}/>
Thanks alot Dat Tran, now i just change my index.js code into this :
import React from "react";
import ReacrDOM from "react-dom";
import {Router, Route, IndexRoute, hashHistory, browserHistory} from "react-router"
import {Provider} from "react-redux";
import {login_check, show_log, fakeLogin} from "./actions/logActions";
import store from "./store";
import Layout from "./pages/Layout";
import Dashboard from "./pages/Dashboard";
import Login from "./pages/login/index";
import User from "./pages/user/index";
import Pemain from "./pages/pemain/index";
const app = document.getElementById("app");
function requireLogin() {
const {user_id, session_key}=store.getState().logReducer;
store.dispatch(login_check({user_id: user_id, session_key: session_key}))
const {logged_in}=store.getState().logReducer;
if (!logged_in) {
browserHistory.push('#/login');
hashHistory.push('/login');
}
}
ReacrDOM.render(
<Provider store={store}>
<Router history={hashHistory}>
<Route path="/login" component={Login}/>
<Route path="/" component={Layout} onEnter={requireLogin}>
<IndexRoute component={Dashboard}/>
<Route path="user" component={User}/>
<Route path="pemain" component={Pemain}/>
</Route>
</Router>
</Provider>
, app);
and my login.js into this :
import React from "react";
import {connect} from "react-redux";
import {login} from "../../actions/logActions";
import {browserHistory, hashHistory} from "react-router"
#connect((store) => {
return {
session_key: store.logReducer.session_key,
user_id: store.logReducer.user_id,
error_message: store.logReducer.error_message,
logged_in: store.logReducer.logged_in,
log_error: store.logReducer.log_error,
}
})
export default class index extends React.Component {
login(event) {
event.preventDefault();
this.props.dispatch(login(this.refs.email_pemain.value, this.refs.password.value));
}
showError() {
if (this.props.log_error) {
return (
<div className="error_message alert-danger">
{this.props.log_error}
</div>
);
}
return (<div>
</div>);
}
render() {
if (this.props.logged_in) {
browserHistory.push('#/');
hashHistory.push('/');
}
return (
<div className="container">
<div className="login-box">
<div className="header">
login
</div>
<div className="login-form-wrapper">
<form onSubmit={this.login.bind(this)}>
<div>
<label for="email_pemain">Email</label>
<input className="form-control" id="email_pemain" name="email_pemain" type="text"
ref="email_pemain"/>
</div>
<div>
<label for="password">Password</label>
<input className="form-control" id="password" name="password" type="password"
ref="password"/>
</div>
<br/>
<br/>
<input className="btn btn-info pull-right" type="submit" value="Login"/>
<div className="clearfix">
</div>
{this.showError()}
</form>
</div>
</div>
</div>
);
}
}
and it works now...and thank you very much #Dat Tran
You need to implement OnEnter props in the routers. Check this project for the whole sollutions:
React Redux Universal Hot Example

Categories

Resources