I have a component called admin it is a form that will redirect me to another page rendering not working though.
Router main
const globalState = {
isAuthed: false,
token: null,
};
export const AuthContext = React.createContext(globalState);
function App() {
const [currentUser, setCurrentUser] = useState(globalState)
return (
<AuthContext.Provider value={[currentUser, setCurrentUser]}>
<Router>
<Switch>
<Route exact path="/admin" component={Admin} />
<Route exact path="/admin-panel" component={Pannel} />
</Switch>
</Router>
</AuthContext.Provider>
)
}
export default App;
admin component
const LoginForm = () => {
const [state, setState] = useContext(AuthContext)
const login = (state) => {
const user = document.getElementById('user').value;
const pass = document.getElementById('pass').value;
const request = {
user,
pass
}
console.log(request)
fetch('/api/admin', {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(request),
})
.then(res => res.json())
.then(res => {
if(res.auth){
valid(5000,"Login Success. Redirecting in 3 second")
setTimeout(() =>{
setState({isAuthed: res.auth, token: res.key})
}, 3000)
}
else{
warn(5000,res.message)
}
})
}
return(
<div style={css}>
<ToastContainer />
{(state && state.isAuthed)? <Redirect to='/adming-panel'/>: false}
<h1 style={{color: "teal"}}>Admin Panel</h1>
<Form id="login-form" size='large' style={{backgroundColor: "white"}}>
<Segment stacked>
<Form.Input id="user" fluid icon='user' iconPosition='left' placeholder='E-mail address' />
<Form.Input
fluid
icon='lock'
iconPosition='left'
placeholder='Password'
type='password'
id="pass"
/>
<Button onClick={() => login(state)} color='teal' fluid size='large'>
Login
</Button>
</Segment>
</Form>
</div>
)
}
export default LoginForm
the new page that I want to render
const Pannel = () => {
const [state, setState] = useContext(AuthContext)
return (
<div>
{(!state || !state.isAuthed)? <Redirect to='/adming-panel'/>: false}
Secret Page
</div>
)
}
export default Pannel
All the answers that I searched for. Was to put the exact keyword before the path but still, the component won't render only an empty white screen appears and no errors on console or backend console.
<Route exact path="/admin-panel" component={Pannel} />
Spot the key difference
<Redirect to='/adming-panel'/>
You are welcome.
Related
This question already has answers here:
How to create a protected route with react-router-dom?
(5 answers)
Closed 21 days ago.
I have a problem now because it won't navigate me after I click log in, it takes cookies, but won't relocate. You can show my components look right now. Everything is fine, except when I click log in, it won't relocates me to the home page.
Components:
<App /> component
function App() {
const [userData, setUserData] = useState();
const [login, setLogin] = useState()
return (
<div className="App">
<Router>
<Routes>
<Route element={<PrivateRoutes login={login} userData=
{userData} />}>
<Route element={<Home />} path="/" exact />
<Route element={<Products />} path="/products" />
</Route>
<Route element={<Login setUserData={setUserData} setLogin=
{setLogin} />}
path="/login" />
</Routes>
</Router>
</div>
);
}
<Login /> component
const Login = ({ setUserData }) => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [checking, setChecking] = useState(true)
const [loginStatus, setLoginStatus] = useState("");
Axios.defaults.withCredentials = true;
const login = (e) => {
e.preventDefault();
Axios.post("http://localhost:3001/login", {
username: username,
password: password,
}).then((response) => {
setUserData(response.data);
navigate("/");
});
};
useEffect(() => {
if (userData?.loggedIn) {
navigate("/");
}
}, [userData]);
}
return (
<div>
<div>Prijava</div>
<div>
<form onSubmit={login}>
<label>Korisničko ime</label>
<input
type="text"
onChange={(e) => {
setUsername(e.target.value);
}}
/>
<label>Lozinka</label>
<input
type="password"
onChange={(e) => {
setPassword(e.target.value);
}}
></input>
<button>Prijavi se</button>
</form>
</div>
</div>
);
};
<PrivateRoutes /> component
const PrivateRoutes = ({userData, login}) => {
return !login ? <p>Checking...</p>: userData?.loggedIn ? <Outlet
/> : <Navigate to="/login" />;
};
Console log error, after implementing code.
Newest App and PrivateRoutes version
When the page loads, userData is not set, because the useEffect runs after the JSX is rendered. Hence your condition is falling to <Navigate to="/login" /> instead of <Outlet/>.
You could use an additional state, called checking, for example, use it to display some loading message while the data is fetched.
Also, move the useEffect to check the login state on load inside PrivateRoutes. Changes your components as follows.
App:
function App() {
const [userData, setUserData] = useState();
const [checking, setChecking] = useState(true);
useEffect(() => {
Axios.get("http://localhost:3001/login")
.then((response) => {
if (response.data.loggedIn == true) {
setUserData(response.data);
}
return;
})
.catch((error) => {
console.log(error);
})
.finally(() => {
setChecking(false);
});
}, []);
return (
<div className="App">
<Router>
<Routes>
<Route element={<PrivateRoutes userData={userData} checking={checking} />}>
<Route element={<Home />} path="/" exact />
<Route element={<Products />} path="/products" />
</Route>
<Route element={<Login setUserData={setUserData} userData={userData} />} path="/login" />
</Routes>
</Router>
</div>
);
}
PrivateRoutes:
const PrivateRoutes = ({ userData, checking }) => {
return checking ? <p>Checking...</p> : userData?.loggedIn ? <Outlet /> : <Navigate to="/login" />;
};
Login:
import { useNavigate } from "react-router-dom";
const Login = ({ setUserData, userData }) => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();
Axios.defaults.withCredentials = true;
const login = (e) => {
e.preventDefault();
Axios.post("http://localhost:3001/login", {
username: username,
password: password,
}).then((response) => {
setUserData(response.data);
});
};
useEffect(() => {
if (userData) {
navigate("/");
}
}, [userData]);
return (
<div>
<form>
<div>Prijava</div>
<div>
<form onSubmit={login}>
<label>Korisničko ime</label>
<input
type="text"
onChange={(e) => {
setUsername(e.target.value);
}}
/>
<label>Lozinka</label>
<input
type="password"
onChange={(e) => {
setPassword(e.target.value);
}}
></input>
<button>Prijavi se</button>
</form>
</div>
</form>
</div>
);
};
To resolve the issue with the userData state being undefined during the initial render, you can initialize it with a default value in the state declaration, for example:
const [userData, setUserData] = useState({ loggedIn: false });
Additionally, you can wrap the conditional rendering in a useEffect hook that depends on userData:
useEffect(() => {
if (userData?.loggedIn) {
// Render the Outlet component
} else {
// Render the Navigate component
}
}, [userData]);
I'm using Laravel on the backend and react.js on the frontend , when I try to login it will redirect to the home ("/") but when I refresh the page I am redirected to login page again. I am using rote protection file which takes component as a parameter and all the components passed in protected route are only accessible by logged in users.. after logging in when I try to access protected pages I am redirected to login page.. can any body help?
app.js
function App() {
return (
<div className="App">
{/* <AuthContextProvider> */}
<BrowserRouter>
<Switch>
<Route path = "/login"><Login/></Route>
<Route path= "/register"><Register/></Route>
<Route path = "/add"><AuthProtection component = {AddProduct}/> </Route>
<Route path = "/update"><AuthProtection component = {UpdateProduct}/> </Route>
<Route path = "/search"><AuthProtection component = {Search}/> </Route>
<Route path = "/"><AuthProtection component = {Home}/> </Route>
</Switch>
</BrowserRouter>
{/* </AuthContextProvider> */}
</div>
);
}
export default App;
login.js
export const Login = () => {
const history = useHistory()
const [email, setemail] = React.useState("")
const [password, setpass] = React.useState("")
const loginuser = e =>{
e.preventDefault();
axios.defaults.withCredentials = true;
let data = {email,password}
axios.get('http://localhost:8000/sanctum/csrf-cookie').then(response => {
console.log(response)
axios.post('http://localhost:8000/login',data ,{
headers: {
'Content-Type': 'application/json',
"Accept": "application/json",
'X-XSRF-TOKEN': (() => {
const name = 'XSRF-TOKEN'
const cookies = '; ' + document.cookie
const parts = cookies.split('; ' + name + '=')
const value = parts.length == 2 ? parts.pop().split(';').shift() : null
console.log('>>> XSRF-TOKEN', value)
return value
})(),
},
}).then(response => {
localStorage.setItem("user-info" , JSON.stringify(response.config.headers["X-XSRF-TOKEN"]))
Auth.authenticate();
history.push("/");
});
});
}
return (
<>
<Header/>
<div>
<div className="wrapper fadeInDown">
<div id="formContent">
{/* Tabs Titles */}
{/* Icon */}
<div className="fadeIn first">
</div>
{/* Login Form */}
<form onSubmit = {loginuser}>
<input type="text" value={email} onChange={(e)=>setemail(e.target.value)} className="fadeIn second" placeholder="login" />
<input type="password" value={password} onChange={(e)=>setpass(e.target.value)} className="fadeIn third" placeholder="password" />
<button type="submit" className="fadeIn fourth btn btn-primary" >LOGIN</button>
</form>
{/* Remind Passowrd */}
<div id="formFooter">
<a className="underlineHover" href="#">Forgot Password?</a>
</div>
</div>
</div>
</div>
</>
)
}
Auth.js
const Auth = {
isAuthenticated: false,
authenticate() {
this.isAuthenticated = true;
},
signout() {
this.isAuthenticated = false;
},
getAuth() {
return this.isAuthenticated;
}
};
export default Auth;
protectedRoutes.js
import React from 'react'
import { useHistory } from 'react-router'
import Auth from './Auth'
import { AuthContext } from './AuthContext';
export const AuthProtection = (props) => {
console.log(props)
const history = useHistory()
let ProtectedTemplate = props.component
console.log(Auth.getAuth())
React.useEffect(()=>{
if( !Auth.getAuth() ){
history.push("/login")
}
})
return (
<div>
<ProtectedTemplate/>
</div>
)
}
trying to use the handle submit function to change the isAuthinticated to true from false thats on the main app.js file thats using react router.. all of the redux examples i look at are all using an app js file that has the onclick function on it not react router. thank you if you can help
function App () {
return (
<Router>
<div>
<Switch>
<Route exact path="/" component={Login} />
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={Signup} />
<PrivateRoute
exact path="/mainpage"
component={MainPage}
isAuthenticated={false}
/>
</Switch>
<Footer />
</div>
</Router>
);
}
export default App;
my login.js that has the click event
const Login = () => {
const history = useHistory()
const [state, setState] = useState({
email: '',
password: ''
});
const [validate, setValid] = useState({
validateEmail: '',
validatePassword: '',
})
const handleInputChange = event => setState({
...state,
[event.target.name]: event.target.value,
})
const handleSubmit = user => {
if(state.password === ''){
setValid({validatePassword:true})
}
if(state.email === '' ){
setValid({validateEmail:true})
}
axios.post(`auth/login`, state )
.then(res => {
console.log(res);
console.log(res.data);
if (res.status === 200 ) {
history.push('/mainpage');
}
})
.catch(function (error) {
console.log(error);
alert('Wrong username or password')
window.location.reload();
});
}
// console.log('state', state)
const {email, password } = state
const [popoverOpen, setPopoverOpen] = useState(false);
const toggle = () => setPopoverOpen(!popoverOpen);
return (
<>
<Container>
<Row>
<Col>
<img src={logo} alt="Logo" id="logo" />
<Button id="Popover1" type="button">
About Crypto-Tracker
</Button>
<Popover placement="bottom" isOpen={popoverOpen} target="Popover1" toggle={toggle}>
<PopoverHeader>About</PopoverHeader>
<PopoverBody>Your personalized finance app to track all potential cryptocurrency investments</PopoverBody>
</Popover>
</Col>
<Col sm="2" id="home" style={{height: 500}}>
<Card body className="login-card">
<Form className="login-form">
<h2 className="text-center">Welcome</h2>
<h3 className="text-center">____________</h3>
<FormGroup>
<Label for="exampleEmail">Email</Label>
<Input invalid={validate.validateEmail} onChange = {handleInputChange} value = {email} type="email" required name="email" placeholder="email" />
<FormFeedback>Please enter email</FormFeedback>
</FormGroup>
<FormGroup>
<Label for="examplePassword">Password</Label>
<Input invalid={validate.validatePassword} onChange = {handleInputChange} value = {password} type="password" required name="password" placeholder="password"/>
<FormFeedback>Please enter password</FormFeedback>
</FormGroup>
<Button onClick={()=> handleSubmit(state)} className="but-lg btn-dark btn-block">Login</Button>
<div className="text-center pt-3"> or sign in with Google account</div>
<Loginbutton />
<div className="text-center">
Sign up
<span className="p-2">|</span>
Forgot password
</div>
</Form>
</Card>
</Col>
</Row>
</Container>
</>
);
}
export default Login;
authslice.js using asyncthunk
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit'
import { userAPI } from '../../../server/routes/authRoutes'
const fetchAuth = createAsyncThunk(
'auth/login',
async (userId, thunkAPI) => {
const response = await userAPI.fetchById(userId)
return response.data
}
)
const authSlice = createSlice({
name: 'isAuthenticated',
initialState: {
value: false,
},
reducers: {
// Authenticated: state => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
// state.value = true;
// },
},
extraReducers: {
[fetchAuth.fulfilled]: state => {
// Add user to the state array
state.value = true
},
[fetchAuth.rejected]: state => {
// Add user to the state array
state.value = false
}
}
});
dispatch(fetchUserById(123))
// export const { increment, decrement, incrementByAmount } = counterSlice.actions;
This would be easy to achieve if you use some kind of global store to store such values. Typical options include Redux, Mobx, etc.
Define your isAuthenticated variable as a global store variable. mutate its value using corresponding package methods like Redux dispatch events or similar from your functions anywhere else in the app.
You can try it using Redux, creating a store to the login variables, and sharing it with the component App.js.
Here's my app component that renders an input for user to type in. What I want to achieve is that whatever input that the user writes in will be passed down to the Films component. That text is then will be used as a query param to get data from an api endpoint
import React, { useState } from "react";
import Films from "../Films";
import { BrowserRouter as Router, Route } from "react-router-dom";
import "./style.css";
const App = () => {
const [text, setText] = useState("");
const [redirect, setRedirect] = useState(false);
const onSubmit = (event) => {
setRedirect(true);
};
return (
<Router>
<div>
<p className="films-analysis-service">Films Analysis Service </p>
<form id="input-form" onSubmit={onSubmit}>
<input
type="text"
id="input-box"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<button className="button" type="submit">
Search
</button>
</form>
{redirect && (
<Route
exact
path="/films"
render={(props) => <Films text={text} />}
/>
)}
</div>
</Router>
);
};
export default App;
This is my Films component:
import React, { useEffect, useState } from "react";
import "./style.css";
const axios = require("axios");
const Films = ({ text }) => {
const [movies, setMovies] = useState([]);
const fetchMoviesByDirectorName = () => {
let config = {
headers: { Authorization: apiKey },
params: {
directorName: text, //the text that is passed down from App component
},
};
axios.get(filmsEndpointURL, config);
};
useEffect(() => {
fetchMoviesByDirectorName();
}, [movies]);
return (
...
);
};
export default Films;
At the moment I'm just trying to print out the data that I would get back but unfortunately I don't think my input value is passed down properly. I don't think my usage of Route is correct.. and it's not redirecting to /films component. What changes should I do to my App.js?
Make sure to do event.preventDefault() in onSubmit
const onSubmit = (event) => {
event.preventDefault(); //<----- like this
setRedirect(true);
};
You can use Redirect to navigate to Films not Route.
Refactored code (using Redirect)
<Router>
<div>
<p className="films-analysis-service">Films Analysis Service </p>
<form id="input-form" onSubmit={onSubmit}>
<input
type="text"
id="input-box"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<button className="button" type="submit">
Search
</button>
</form>
<Route exact path="/films" render={(props) => <Films text={text} {...props}/>} /> //<----see here
{redirect && <Redirect to="/films" />} //<----see here
</div>
<Route></Route>
</Router>;
You can also use history object to navigate to Films
import { createBrowserHistory } from "history";
const customHistory = createBrowserHistory();
const App = () => {
const [text, setText] = useState("");
const [redirect, setRedirect] = useState(false);
const onSubmit = (event) => {
// setRedirect(true);//<----no need
customHistory.push("/films");
};
return (
<Router history={customHistory}>
<div>
<p className="films-analysis-service">Films Analysis Service </p>
<form id="input-form" onSubmit={onSubmit}>
<input
type="text"
id="input-box"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<button className="button" type="submit">
Search
</button>
</form>
<Route exact path="/films" render={(props) => <Films text={text} />} />
</div>
</Router>
);
};
export default App;
I'm trying to redirect to login page if not logged when trying to access any components that require authentication.
While trying to use firebase auth, the auth object is not always initialised and there is a small delay so according to documentation, I have to use onAuthStateChanged() event listener which all works ok with the Nav bar which shows the signed in user.
However, when trying to to do the redirection for loggedin users, it's not working as the initial login dialog is being shown always as the state for authuser initially is not set and the routing seems to have already taken place. I seen a similar question (Firebase, Avoid Logged-in user to visit login page) here but don't really understand how I can use the suggestion cookies/querystrings from one of the comments when going directly to the url as there will still be small delay initially between the session value being set and rendering so don't see how it will work first time using that way. Totally new to React so hope this question makes sense.
PrivateRoute.js :
// This is used to determine if a user is authenticated and
// if they are allowed to visit the page they navigated to.
// If they are: they proceed to the page
// If not: they are redirected to the login page.
import React, {useState} from 'react'
import { Redirect, Route } from 'react-router-dom'
const PrivateRoute = ({ loggedInUser, component: Component, ...rest }) => {
console.log("Private Route, Logged User:"+JSON.stringify(loggedInUser))
return (
<Route
{...rest}
render={props =>
(loggedInUser) ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: '/login', state: { from: props.location } }} />
)
}
/>
)
}
export default PrivateRoute;
I have never worked with firebase, but I am doing something like this.
Let's say I have custom hooks, where I am getting my token, login, logout functions.
Here is my app.js looks like. I am getting token if user authorized to access. If access granted it gets the token, then sets the routes and redirects to admin dashboard.
function App() {
const { token, login, logout } = useAuth();
let routes;
if (token) {
routes = (
<Switch>
<Redirect to="/dashboard" />
</Switch>
);
} else {
routes = (
<Switch>
<Redirect to="/login" />
<Route path="/" exact component={Layout} />
</Switch>
)
}
return (
<AuthContext.Provider
value={{
isLoggedIn: !!token,
token: token,
login: login,
logout: logout,
}}
>
<Navigation />
<Router>
<main>
{routes}
</main>
</Router>
</AuthContext.Provider>
);
}
And this how my nav looks like
const Navigation = () => {
const auth = useContext(AuthContext);
return (
<>
{ auth.isLoggedIn && (<AdminNavigation />) }
{ !auth.isLoggedIn && (<UserNavigation />) }
</>
);
};
This is how my SignIn component looks like
const SignIn = () => {
const auth = useContext(AuthContext);
const classes = useStyles();
const {
// eslint-disable-next-line no-unused-vars
isLoading, error, sendRequest, clearError,
} = useHttpClient();
const [password, setPassword] = useState('');
const [email, setEmail] = useState('');
const [isLoggedIn, setIsLoggedIn] = useState(true);
const updateInput = (event) => {
if (event.target.id.toLowerCase() === 'email') {
setEmail(event.target.value);
} else {
setPassword(event.target.value);
}
};
const handleSwitchMode = () => {
setIsLoggedIn((prevMode) => !prevMode);
};
const authSubmitHandler = async (event) => {
event.preventDefault();
setIsLoggedIn(true);
// auth.login();
if (isLoggedIn) {
try {
const responseData = await sendRequest(
`${API_URL}/admin/login`,
'POST',
JSON.stringify({
email,
password,
}),
{
'Content-Type': 'application/json',
},
);
setIsLoggedIn(false);
auth.login(responseData.token);
} catch (err) {
setIsLoggedIn(false);
}
} else {
try {
const responseData = await sendRequest(
`${API_URL}/admin/signup`,
'POST',
JSON.stringify({
// name,
email,
password,
}),
{
'Content-Type': 'application/json',
},
);
auth.login(responseData.token);
} catch (err) {
clearError();
}
}
};
return (
<Container component="main" maxWidth="xs">
<div className={classes.paper}>
<Typography component="h1" variant="h5">
{isLoggedIn ? 'Login' : 'Sign up'}
</Typography>
<form className={classes.form} noValidate>
{!isLoggedIn && (
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="name"
label="Your name"
autoFocus
onChange={updateInput}
/>
)}
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
autoFocus
value={email}
onChange={updateInput}
/>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
value={password}
autoComplete="current-password"
onChange={updateInput}
/>
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick={authSubmitHandler}
>
{isLoggedIn ? 'Login' : 'Sign up'}
</Button>
<Grid container>
<Grid item xs>
<Link href="/" variant="body2">
Forgot password?
</Link>
</Grid>
<Grid item>
<Button
onClick={handleSwitchMode}
>
Dont have an account? Sign Up
</Button>
</Grid>
</Grid>
</form>
</div>
</Container>
);
Hope this helps.