Route Protection in React js not working well - javascript

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>
)
}

Related

Why in AuthContext.Provider does't set data after login

If I set in context provider sample data, I see this data in all nested components.
But I need login to the account and in response, I get data about user for set in the global context and use in all components.
context/AuthProvider.tsx
const AuthContext = createContext<any>({});
export const AuthProvider = ({ children }: any) => {
const [auth, setAuth] = useState({});
return (
<>
<AuthContext.Provider value={{ auth, setAuth }}>{children}</AuthContext.Provider>
</>
);
};
hooks/useAuth.ts
const useAuth = () => {
return useContext(AuthContext);
};
export default useAuth;
index.tsx
import { AuthProvider } from './context/AuthProvider';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<React.StrictMode>
<AuthProvider>
<App />
</AuthProvider>
</React.StrictMode>
);
I have App with BrowserRouter logic for not logged users redirect to login. If logged, so go to the Homepage.
components/App/App.tsx
const AppContainer: FC<any> = () => {
const { token } = useToken();
return (
<>
<div className={'wrapper'}>
<BrowserRouter>
{!token ? <LoggedOutRoutes /> : <LoggedInRoutes />}
</BrowserRouter>
</div>
</>
);
};
const LoggedOutRoutes: FC<any> = () => (
<Switch>
<Route path="/" exact={true}>
<Login />
</Route>
<Redirect from={'*'} to={'/'} />
</Switch>
);
const LoggedInRoutes: FC = () => (
<Switch>
<Route path="/" exact={true} component={Homepage} />
</Switch>
);
In login component sending request with data and I getting access_token and user data. Now I need set user data in useAuth hook.
const Login: FC<any> = () => {
const { setToken } = useToken();
const { setAuth } = useAuth()
const handleSubmit = async (event: any) => {
event.preventDefault();
const res = await API.login({
login,
password
});
const { access_token, user } = res;
setToken(access_token);
setAuth(user);
window.location.reload();
};
return (
<form onClick={handleSubmit}>
// ...There i have submit form, not interesting
</form>
);
};
After reloading the page, my page will be Homepage where I won't use my context data from the provider but I have an empty object, why?
The problem is window.location.reload. Any SPA will not retain data after a page refresh by default.
Now if you still want to persist that information even after page reload, i recommend to persist that info in localStorage. So something like this should work.
export const AuthProvider = ({ children }: any) => {
const [auth, setAuth] = useState(localStorage.get('some-key') || {});
const updateAuth = (auth) => {
localStorage.set('some-key', auth);
setAuth(auth);
}
return (
<>
<AuthContext.Provider value={{ auth, updateAuth }}>{children}</AuthContext.Provider>
</>
);
};

Pagination is not working in React Frontend

Pagination is not working in React Frontend. I am creating an Ecommerce Application and want to Paginate my products on my website. Here is the Code for Getting Product with Pagination.
const getProducts = asyncHandler(async (req, res, next) => {
const pageSize = 3;
const page = Number(req.query.pageNumber) || 1;
const keyword = req.query.keyword
? {
name: {
$regex: req.query.keyword,
$options: 'i',
},
}
: {};
const count = await Product.countDocuments({ ...keyword });
const products = await Product.find({ ...keyword })
.limit(pageSize)
.skip(pageSize * (page - 1))
.sort({ _id: -1 });
res.json({ products, page, pages: Math.ceil(count / pageSize) });
});
I am using Redux for state Management. Here is the code for the Reducer
// PRODUCT LIST
export const productListReducer = (state = { products: [] }, action) => {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
return { loading: true, products: [] };
case PRODUCT_LIST_SUCCESS:
return {
loading: false,
pages: action.payload.pages,
page: action.payload.page,
products: action.payload.products,
};
case PRODUCT_LIST_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
And here is the Action code
// PRODUCT LIST
export const listProduct =
(keyword = ' ', pageNumber = ' ') =>
async (dispatch) => {
try {
dispatch({ type: PRODUCT_LIST_REQUEST });
const { data } = await axios.get(
`/api/products?keyword=${keyword}&pageNumber=${pageNumber}`
);
dispatch({ type: PRODUCT_LIST_SUCCESS, payload: data });
} catch (error) {
dispatch({
type: PRODUCT_LIST_FAIL,
payload:
error.response && error.response.data.error
? error.response.data.error
: error.message,
});
}
};
And this is the Component for pagination and few others
import React, { useEffect } from 'react';
import { Link } from 'react-router-dom';
import Rating from './Rating';
import Pagination from './pagination';
import { useDispatch, useSelector } from 'react-redux';
import { listProduct } from '../../Redux/Actions/ProductActions';
import Loading from '../LoadingError/Loading';
import Message from '../LoadingError/Error';
const ShopSection = (props) => {
const { keyword, pagenumber } = props;
const dispatch = useDispatch();
const productList = useSelector((state) => state.productList);
const { loading, error, products, page, pages } = productList;
useEffect(() => {
dispatch(listProduct(keyword, pagenumber));
}, [dispatch, keyword, pagenumber]);
return (
<>
<div className='container'>
<div className='section'>
<div className='row'>
<div className='col-lg-12 col-md-12 article'>
<div className='shopcontainer row'>
{loading ? (
<div className='mb-5'>
<Loading />
</div>
) : error ? (
<Message variant='alert-danger'>{error}</Message>
) : (
<>
{products.map((product) => (
<div
className='shop col-lg-4 col-md-6 col-sm-6'
key={product._id}
>
<div className='border-product'>
<Link to={`/products/${product._id}`}>
<div className='shopBack'>
<img src={product.image} alt={product.name} />
</div>
</Link>
<div className='shoptext'>
<p>
<Link to={`/products/${product._id}`}>
{product.name}
</Link>
</p>
<Rating
value={product.rating}
text={`${product.numReviews} reviews`}
/>
<h3>${product.price}</h3>
</div>
</div>
</div>
))}
</>
)}
{/* Pagination */}
<Pagination
pages={pages}
page={page}
keyword={keyword ? keyword : ''}
/>
</div>
</div>
</div>
</div>
</div>
</>
);
};
export default ShopSection;
And Pagination Component is here
import React from 'react';
import { Link } from 'react-router-dom';
const Pagination = (props) => {
const { page, pages, keyword = '' } = props;
return (
pages > 1 && (
<nav>
<ul className='pagination justify-content-center'>
{[...Array(pages).keys()].map((x) => (
<li
className={`page-item ${x + 1 === page ? 'active' : ''}`}
key={x + 1}
>
<Link
className='page-link'
to={
keyword
? `/search/${keyword}/page/${x + 1}`
: `/page/${x + 1}`
}
>
{x + 1}
</Link>
</li>
))}
</ul>
</nav>
)
);
};
export default Pagination;
And here is my App.js component
import React from 'react';
import './App.css';
import './responsive.css';
import 'react-toastify/dist/ReactToastify.css';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import HomeScreen from './screens/HomeScreen';
import SingleProduct from './screens/SingleProduct';
import Login from './screens/Login';
import Register from './screens/Register';
import CartScreen from './screens/CartScreen';
import ShippingScreen from './screens/ShippingScreen';
import ProfileScreen from './screens/ProfileScreen';
import PaymentScreen from './screens/PaymentScreen';
import PlaceOrderScreen from './screens/PlaceOrderScreen';
import OrderScreen from './screens/OrderScreen';
import NotFound from './screens/NotFound';
import PrivateRouter from './PrivateRoute';
const App = () => {
return (
<Router>
<Switch>
<Route path='/' component={HomeScreen} exact />
<Route path='/search/:keyword' component={HomeScreen} exact />
<Route path='/page/:pagenumber' component={HomeScreen} exact />
<Route
path='/search/:keyword/page/:pageNumber'
component={HomeScreen}
exact
/>
<Route path='/products/:id' component={SingleProduct} />
<Route path='/login' component={Login} />
<Route path='/register' component={Register} />
<PrivateRouter path='/profile' component={ProfileScreen} />
<Route path='/cart/:id?' component={CartScreen} />
<PrivateRouter path='/shipping' component={ShippingScreen} />
<PrivateRouter path='/payment' component={PaymentScreen} />
<PrivateRouter path='/placeorder' component={PlaceOrderScreen} />
<PrivateRouter path='/order/:id' component={OrderScreen} />
<Route path='*' component={NotFound} />
</Switch>
</Router>
);
};
export default App;
When i try to send request from postman the data is returning fine. I have already debug alot and couldn't able to resolve this bug. Help me to resolve this i will be grateful to you
You have to define pagenumber, because it is not defined.
const pagenumber = match.params.pagenumber;
Define it like this.
I hope it will work.

Redirect Login React

I want to implement redirect in my react project login. I googled it, and all use router. I've tried to add it to my code but I totally failed. This is my code, how can I implement router in the correct way?
App.js
import React, {useState, useEffect} from 'react';
import { Switch, Route, Redirect, Router } from 'react-router-dom';
import fire from './fire';
import Login from './Login';
import Home from './Home';
import './App.css';
function App() {
const [user, setUser] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [emailError, setEmailError] = useState('');
const [passwordError, setPasswordError] = useState('');
const [hasAccount,setHasAccount] = useState(false); //login e logout
const inputCleaner = () =>{
setEmail("");
setPassword("");
};
const clearErrors = () =>{
setEmailError("");
setPasswordError("");
};
const handleLogin = () =>{
clearErrors();
fire
.auth()
.signInWithEmailAndPassword(email,password)
.catch(err=>{
switch(err.code){
case "auth/invalid-email":
case "auth/user-disabled":
case "auth/user-not-found":
setEmailError(err.message);
break;
case "auth/wrong-password":
setPasswordError(err.message);
break;
}
});
};
const handleSignup = () =>{
clearErrors();
fire
.auth()
.createUserWithEmailAndPassword(email,password)
.catch(err=>{
switch(err.code){
case "auth/email-already-in-use":
case "auth/invalid-email":
setEmailError(err.message);
break;
case "auth/weak-password":
setPasswordError(err.message);
break;
}
});
};
const handleLogout = () =>{
fire.auth().signOut();
};
const authListerner = () =>{
fire.auth().onAuthStateChanged(user => {
if(user){
inputCleaner();
setUser(user);
}else{
setUser("");
}
})
};
useEffect (() =>{
authListerner();
},[]);
return (
<Router>
<div className="App">
{user ? (
<Home handleLogout={handleLogout} />
):(
<Login
email={email}
setEmail={setEmail}
password={password}
setPassword={setPassword}
handleLogin={handleLogin}
handleSignup={handleSignup}
hasAccount={hasAccount}
setHasAccount={setHasAccount}
emailError={emailError}
passwordError={passwordError}
/>
)}
</div>
</Router>
);
}
export default App;
Login.js
import React from 'react'
const Login = (props) =>{
const{
email,
setEmail,
password,
setPassword,
handleLogin,
handleSignup,
hasAccount,
setHasAccount,
emailError,
passwordError
} = props;
return(
<section className="login">
<div className="loginContainer">
<label>Email</label>
<input
type="text"
autoFocus
required
value ={email}
onChange ={e => setEmail(e.target.value)}
/>
<p className="errorMsg">{emailError}</p>
<label>Password</label>
<input
type="password"
autoFocus
required
value ={password}
onChange ={e => setPassword(e.target.value)}
/>
<p className="errorMsg">{passwordError}</p>
<div className="btnContainer">
{hasAccount ? (
<>
<button onClick={handleLogin}>Accedi</button>
<p>Don't have an account?<span onClick={ () => setHasAccount(!hasAccount)}>Sign up</span></p>
</>
) : (
<>
<button onClick={handleSignup}>Sign up</button>
<p>Have an account?<span onClick={ () => setHasAccount(!hasAccount)} >Sign in</span></p>
</>
)}
</div>
</div>
</section>
)
};
export default Login;
Home.js
import React from 'react';
const Home = ({handleLogout}) =>{
return(
<section className="home">
<nav>
<h2>Benvenuti</h2>
<button onClick={handleLogout}>Esci</button>
</nav>
</section>
);
};
export default Home;
I need to redirect to home after login, because I have to hidden the private information about users. To autenticate, it runs firebase, and I've written the functions in another component named fire.js, but to my mind isn't relevant to solve this problem.
Here is an example for you:
<Switch>
<Route exact path="/">
{user ? <Redirect to="/Home" /> : <Login />}
</Route>
</Switch>

React router dom - Form onSubmit pass input value to another component as well as redirect to that component

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;

React Router not rendering page

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.

Categories

Resources