Question about PrivateRoutes and to get navigated back to login page - javascript

Hi I need help getting my code to work so that when I try to log back in I won't be able to view the dashboard since I logged out. Right now its giving me a blank screen in my project and I think its because privateroute isn't a thing anymore? Not sure. This is my code in PrivateRoute.js:
import React, { Component } from 'react';
import { Route, Navigate } from 'react-router-dom';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
const PrivateRoute = ({ component: Component, auth: { isAuthenticated, loading },
...rest }) => (
<Route {...rest} render={props => !isAuthenticated && !loading ? (<Navigate to='/login' />) : (<Component {...props} />)} />
)
PrivateRoute.propTypes = {
auth: PropTypes.object.isRequired
};
const mapStatetoProps = state => ({
auth: state.auth
});
export default connect(mapStatetoProps)(PrivateRoute);
This is the code for the app.js:
import React, { Fragment, useEffect } from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Navbar from './components/layout/Navbar';
import Landing from './components/layout/Landing';
import Register from './components/auth/Register';
import Login from './components/auth/Login';
import Alert from './components/layout/Alert';
import Dashboard from './components/dashboard/Dashboard';
import PrivateRoute from './components/routing/PrivateRoute';
// Redux
import { Provider } from 'react-redux';
import store from './store';
import { loadUser } from './actions/auth';
import setAuthToken from './utils/setAuthToken';
import './App.css';
if (localStorage.token) {
setAuthToken(localStorage.token);
}
const App = () => {
useEffect(() => {
store.dispatch(loadUser());
}, []);
return (
<Provider store={store}>
<Router>
<Fragment>
<Navbar />
<Routes>
<Route exact path='/' element={<Landing/>} />
</Routes>
<section className="container">
<Alert />
<Routes>
<Route exact path='/Register' element={<Register/>} />
<Route exact path='/Login' element={<Login/>} />
<PrivateRoute exact path='/dashboard' element={Dashboard} />
</Routes>
</section>
</Fragment>
</Router>
</Provider>
)};
export default App;
Please Help!

there is a recommended Protected Route implementation in the v6 react router docs you can follow.
DOCS IMPLEMENTATION
Your issue:
<Route {...rest} render={props => !isAuthenticated && !loading ?
(<Navigate to='/login' />) : (<Component {...props} />)} />
This Route is not up to date with v6, which I assume you are using, you need to update it.
Exact is also not supported anymore, there is no point in including it in your routes.
If you are not using v6 please update your post and include your react-router version.

Related

Invalid login-check system with a custom hook

AuthRoute.jsx
import React from 'react';
import { useAuth } from 'react-firebase-hooks/auth';
import { Route, Redirect } from 'react-router-dom';
function AuthRoute({ children, ...rest }) {
const { user } = useAuth();
return (
<Route
{...rest}
render={({ location }) =>
user ? (
children
) : (
<Redirect
to={{
pathname: '/login',
state: { from: location }
}}
/>
)
}
/>
);
}
export default AuthRoute;
App.jsx
import { auth } from "./components/base";
import { useAuthState } from "react-firebase-hooks/auth";
import React from "react";
import Home from "./pages/Home";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Login from "./pages/Login";
import Register from "./pages/Register";
import MainPage from "./pages/MainPage";
import AuthRoute from "./pages/AuthRoute";
function App() {
const [user, loading] = useAuthState(auth);
return (
<div>
<Router>
<Routes>
<Route path="/" element={<Home />} />
<AuthRoute path="/mainpage" exact element={MainPage} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
</Routes>
</Router>
</div>
);
}
export default App;
The point is:
I created a custom hook AuthRoute.jsx which checks if the user is logged in via useAuth.
When a user tries to proceed to MainPage.jsx and if he's not logged in (Login.jsx) (I also connected my application to Firebase Auth so the information should be there), he can't proceed to MainPage.jsx.
However, if the user is logged in, he can proceed to MainPage.jsx.
I am facing this error in console.log:
Uncaught SyntaxError: The requested module '/node_modules/.vite/deps/react-router-dom.js?v=6310222e' does not provide an export named 'Redirect' (at AuthRoute.jsx:3:17)
I also asked ChatGPT to help me fix this issue but his solutions didn't work.
I'd appreciate if you guys can help me correct the code.

React page won't stop refreshing

I recently deployed my site on to a work test server. When I was building everything locally it seemed to be working fine. However once I went ahead and deployed it, the site now auto refreshes none stop almost every second. I have gone through my code and the small changes I made and can't seem to find what the cause of the issue is and why the site on our work vps is now refreshing over and over again. I'm hoping someone might have some insight on this. I have gone ahead and posted my App.jsx, index.js, and Home.jsx below. Any insight on the matter would be really appreciated.
I should also note that I went back and moved everything back local to see if there is something I might have done before sending it to the server. It would seem when I run this locally there are zero issues. I've also tested this on multiple browsers and asked a few colleagues to take a look and it seems it continues to refresh every second for them too and on multiple browsers
App.jsx
import "./app.scss";
import Home from "./pages/home/Home";
import Register from "./pages/register/Register";
import Watch from "./pages/watch/Watch";
import Login from "./pages/login/Login";
import {
BrowserRouter as Router,
Switch,
Route,
Redirect,
} from "react-router-dom";
import { useContext } from "react";
import { AuthContext } from "./context/authContext/AuthContext";
const App = () => {
const { user } = useContext(AuthContext);
return (
<Router>
<Switch>
<Route exact path="/">
{user ? <Home /> : <Redirect to="/register" />}
</Route>
<Route path="/register">
{!user ? <Register /> : <Redirect to="/" />}
</Route>
<Route path="/login">{!user ? <Login /> : <Redirect to="/" />}</Route>
{user && (
<>
<Route path="/movies">
<Home type="movie" />
</Route>
<Route path="/series">
<Home type="series" />
</Route>
<Route path="/watch">
<Watch />
</Route>
</>
)}
</Switch>
</Router>
);
};
export default App;
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { AuthContextProvider } from "./context/authContext/AuthContext";
ReactDOM.render(
<React.StrictMode>
<AuthContextProvider>
<App />
</AuthContextProvider>
</React.StrictMode>,
document.getElementById("root")
);
Home.jsx
import Navbar from "../../components/navbar/Navbar";
import Featured from "../../components/featured/Featured";
import "./home.scss";
import List from "../../components/list/List";
import { useEffect, useState } from "react";
import axios from "axios";
const Home = ({ type }) => {
const [lists, setLists] = useState([]);
const [genre, setGenre] = useState(null);
const axiosInstance = axios.create({
baseURL: process.env.REACT_APP_API_URL,
});
useEffect(() => {
const getRandomLists = async () => {
try {
const res = await axiosInstance.get(
`lists${type ? "?type=" + type : ""}${
genre ? "&genre=" + genre : ""
}`,
{
headers: {
token:
"Bearer " +
JSON.parse(localStorage.getItem("user")).accessToken,
},
}
);
setLists(res.data);
} catch (err) {
console.log(err);
}
};
getRandomLists();
}, [type, genre, axiosInstance]);
return (
<div className="home">
<Navbar />
<Featured type={type} setGenre={setGenre} />
{lists.map((list) => (
<List list={list} />
))}
</div>
);
};
export default Home;
A new axiosInstance is created on every render, and it triggers useEffect change.
Move the axiosInstance creation out of the component.
const axiosInstance = axios.create({
baseURL: process.env.REACT_APP_API_URL,
});
const Home = ({ type }) => {
const [lists, setLists] = useState([]);
const [genre, setGenre] = useState(null);
// stuff
};
Does the route that contains the component refreshes infinitely?
That is because React Refresh causes component remount each time the state changes. In your case, you are creating an axios instance 1) not only not storing it in the useState ( you create it as a plain variable ), 2) but also on each component render;
That causes the React Refresh to go crazy. The reason why it works on local might be related to some Webpack stuff, but that doesn't matter much as you should configure axios differently anyways.
Solution:
Why not to set up default configuration that will be common for all the requests through the application?
import "./app.scss";
import Home from "./pages/home/Home";
import Register from "./pages/register/Register";
import Watch from "./pages/watch/Watch";
import Login from "./pages/login/Login";
import {
BrowserRouter as Router,
Switch,
Route,
Redirect,
} from "react-router-dom";
import { useEffect, useContext } from "react";
import { AuthContext } from "./context/authContext/AuthContext";
const App = () => {
useEffect( () => {
axios.defaults.baseURL = process.env.REACT_APP_API_URL;
}, [] )
const { user } = useContext(AuthContext);
return (
<Router>
<Switch>
<Route exact path="/">
{user ? <Home /> : <Redirect to="/register" />}
</Route>
<Route path="/register">
{!user ? <Register /> : <Redirect to="/" />}
</Route>
<Route path="/login">{!user ? <Login /> : <Redirect to="/" />}</Route>
{user && (
<>
<Route path="/movies">
<Home type="movie" />
</Route>
<Route path="/series">
<Home type="series" />
</Route>
<Route path="/watch">
<Watch />
</Route>
</>
)}
</Switch>
</Router>
);
};
export default App;
So that no instances are created, and defaults are configured correctly.
Then you use it like:
axios.post( '/someendpoint', { test : true } )
Let me know if it works!

React conditional rendering not triggering?

I am trying to change the contents of my Navbar and my Router via useContext and conditional rendering.
This is my App.js:
import "./App.css";
import axios from "axios";
import { AuthContextProvider } from "./context/AuthContext";
import MyRouter from "./MyRouter";
axios.defaults.withCredentials = true;
function App() {
return (
<AuthContextProvider>
<MyRouter />
</AuthContextProvider>
);
}
export default App;
This is my Router:
import React, { useContext } from "react";
import AuthContext from "./context/AuthContext";
import {
BrowserRouter as Router,
Switch,
Route,
Redirect,
} from "react-router-dom";
import MyNavBar from "./components/MyNavbar";
import "./App.css";
import Home from "./components/pages/Home";
import AboutUs from "./components/pages/AboutUs";
import Register from "./components/pages/Register";
import MyFooter from "./components/MyFooter";
import login from "./components/pages/login";
import ProfilePage from "./components/pages/ProfilePage";
function MyRouter() {
const { loggedIn } = useContext(AuthContext);
return (
<Router>
<MyNavBar />
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/about-us" component={AboutUs} />
{loggedIn === false && (
<>
<Route exact path="/register" component={Register} />
<Route exact path="/login" component={login} />
</>
)}
{loggedIn === true && (
<>
<Route exact path="/profile" component={ProfilePage} />
</>
)}
<Redirect to="404" />
</Switch>
<MyFooter />
</Router>
);
}
export default MyRouter;
My Navbar's conditional rendering works in the same way as the router. My problem is that neither of the conditional rendering fragments are working. For example, when my application starts, users are not logged in and the "loggedIn" value is false. With this logic, the routes "register" and "login" should be accessible, but they are not. Any advice or help would be greatly appreciated as I am quite new to using React.
Here is a screenshot of my console upon loading the application
This is my "AuthContext":
const AuthContext = createContext();
function AuthContextProvider(props) {
const [loggedIn, setLoggedIn] = useState(undefined);
async function getLoggedIn() {
const loggedInRes = await axios.get(
"http://localhost:5000/api/users/loggedIn"
);
setLoggedIn(loggedInRes.data);
}
useEffect(() => {
getLoggedIn();
}, []);
return (
<AuthContext.Provider value={{ loggedIn, getLoggedIn }}>
{props.children}
</AuthContext.Provider>
);
}
export default AuthContext;
export { AuthContextProvider };
Thanks for all the help guys, I learned a lot about the ProtectedRoute concept. My issue came from my Navigation Bar component. Instead of declaring my variable as an object, I declared loggedin like
const loggedIn = useContext(AuthContext);
All my errors were solved once I changed it to
const { loggedIn } = useContext(AuthContext);
Edited:
Instead of using your conditions in your main route, you can create a ProtectedRoute which Routes your component according to your condition.
import React, { useEffect } from "react";
import { Route, Redirect, useHistory } from "react-router-dom";
const ProtectedRoute = ({ component: Component, ...rest }) => {
const { loggedIn } = useContext(AuthContext);
return (
<Route
{...rest}
render={(props) =>
loggedIn() ? (
<Component {...props} />
) : (
<Redirect to='/login' />
)
}
/>
);
};
export default ProtectedRoute;
In your component you can change your logic with this:
<ProtectedRoute exact path="/" component={Home} />
<ProtectedRoute exact path="/about-us" component={AboutUs}/>
<Route exact path="/register" component={Register} />
<Route exact path="/login" component={login} />
<ProtectedRoute exact path="/profile" component={ProfilePage} />
This means that if you wanna protect a route you should route it with ProtectedRoute otherwise classic react route.
You can read about that ProtectedRoute concept here

How to handle Authentication with react-router?

Trying to make certain routes require Authentication.
I have this:
class App extends Component {
render() {
const menuClass = `${this.props.contentMenuClass} col-xs-12 col-md-9`;
return (
<BrowserRouter history={browserHistory}>
<div className="App">
<Header properties={this.props} />
<div className="container-fluid">
<div className="row">
<SideNav />
<div className={menuClass} id="mainContent">
<Switch>
{routes.map(prop =>
(
<Route
path={prop.path}
component={prop.component}
key={prop.id}
render={() => (
!AuthenticationService.IsAutheenticated() ?
<Redirect to="/Login"/>
:
<Route path={prop.path}
component={prop.component}
key={prop.id}/>
)}
/>
))}
</Switch>
</div>
</div>
</div>
{/* <Footer /> */}
</div>
</BrowserRouter>
);
}
}
const mapStateToProps = state => ({
contentMenuClass: state.menu,
});
export default connect(mapStateToProps)(App);
Note: Yes the auth service works as it should.
For every route I am checking if the user is authenticated, if not then I want to redirect them to the login page, if they are then it will land on the first page with the route of "/".
All I am getting is:
react-dom.development.js:14227 The above error occurred in the <Route> component:
in Route (created by App)
in Switch (created by App)
in div (created by App)
in div (created by App)
in div (created by App)
in div (created by App)
in Router (created by BrowserRouter)
in BrowserRouter (created by App)
in App (created by Connect(App))
in Connect(App)
in Provider
Where am I doing this wrong?
A simple solution would be to make a HOC (High Order Component) that wraps all protected routes.
Depending upon how nested your app is, you may want to utilize local state or redux state.
Working example: https://codesandbox.io/s/5m2690nn6n (this uses local state)
routes/index.js
import React from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Home from "../components/Home";
import Players from "../components/Players";
import Schedule from "../components/Schedule";
import RequireAuth from "../components/RequireAuth";
export default () => (
<BrowserRouter>
<RequireAuth>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/players" component={Players} />
<Route path="/schedule" component={Schedule} />
</Switch>
</RequireAuth>
</BrowserRouter>
);
components/RequireAuth.js
import React, { Component, Fragment } from "react";
import { withRouter } from "react-router-dom";
import Login from "./Login";
import Header from "./Header";
class RequireAuth extends Component {
state = { isAuthenticated: false };
componentDidMount = () => {
if (!this.state.isAuthenticated) {
this.props.history.push("/");
}
};
componentDidUpdate = (prevProps, prevState) => {
if (
this.props.location.pathname !== prevProps.location.pathname &&
!this.state.isAuthenticated
) {
this.props.history.push("/");
}
};
isAuthed = () => this.setState({ isAuthenticated: true });
unAuth = () => this.setState({ isAuthenticated: false });
render = () =>
!this.state.isAuthenticated ? (
<Login isAuthed={this.isAuthed} />
) : (
<Fragment>
<Header unAuth={this.unAuth} />
{this.props.children}
</Fragment>
);
}
export default withRouter(RequireAuth);
Or, instead of wrapping routes, you can create a protected component that houses protected routes.
Working example: https://codesandbox.io/s/yqo75n896x (uses redux instead of local state).
routes/index.js
import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import { createStore } from "redux";
import { Provider } from "react-redux";
import Home from "../components/Home";
import Header from "../containers/Header";
import Info from "../components/Info";
import Sponsors from "../components/Sponsors";
import Signin from "../containers/Signin";
import RequireAuth from "../containers/RequireAuth";
import rootReducer from "../reducers";
const store = createStore(rootReducer);
export default () => (
<Provider store={store}>
<BrowserRouter>
<div>
<Header />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/info" component={Info} />
<Route path="/sponsors" component={Sponsors} />
<Route path="/protected" component={RequireAuth} />
<Route path="/signin" component={Signin} />
</Switch>
</div>
</BrowserRouter>
</Provider>
);
containers/RequireAuth.js
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import ShowPlayerRoster from "../components/ShowPlayerRoster";
import ShowPlayerStats from "../components/ShowPlayerStats";
import Schedule from "../components/Schedule";
const RequireAuth = ({ match: { path }, isAuthenticated }) =>
!isAuthenticated ? (
<Redirect to="/signin" />
) : (
<div>
<Route exact path={`${path}/roster`} component={ShowPlayerRoster} />
<Route path={`${path}/roster/:id`} component={ShowPlayerStats} />
<Route path={`${path}/schedule`} component={Schedule} />
</div>
);
export default connect(state => ({
isAuthenticated: state.auth.isAuthenticated
}))(RequireAuth);
You can even get more modular by creating a wrapper function. You would be able to pick and choose any route by simply wrapping over the component. I don't have a codebox example, but it would be similar to this setup.
For example: <Route path="/blog" component={RequireAuth(Blog)} />
You spelled Autheenticated wrong.
Also, this is an assumption because you only provided the stack trace and not the above error, which probably says AuthenticationService.IsAutheenticated is not a function.

How to redirect to a different page after authenticated with React

I have bought a template in envato which is quite complex, I know a bit of react, I am not an expert yet, however I managed to integrate Azure AD Authentication into the app.
I used this procedure:
https://github.com/salvoravida/react-adal
My app is like this:
DashApp.js
import React from "react";
import { Provider } from "react-redux";
import { store, history } from "./redux/store";
import PublicRoutes from "./router";
import { ThemeProvider } from "styled-components";
import { LocaleProvider } from "antd";
import { IntlProvider } from "react-intl";
import themes from "./settings/themes";
import AppLocale from "./languageProvider";
import config, {
getCurrentLanguage
} from "./containers/LanguageSwitcher/config";
import { themeConfig } from "./settings";
import DashAppHolder from "./dashAppStyle";
import Boot from "./redux/boot";
const currentAppLocale =
AppLocale[getCurrentLanguage(config.defaultLanguage || "english").locale];
const DashApp = () => (
<LocaleProvider locale={currentAppLocale.antd}>
<IntlProvider
locale={currentAppLocale.locale}
messages={currentAppLocale.messages}
>
<ThemeProvider theme={themes[themeConfig.theme]}>
<DashAppHolder>
<Provider store={store}>
<PublicRoutes history={history} />
</Provider>
</DashAppHolder>
</ThemeProvider>
</IntlProvider>
</LocaleProvider>
);
Boot()
.then(() => DashApp())
.catch(error => console.error(error));
export default DashApp;
export { AppLocale };
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import DashApp from './dashApp';
import registerServiceWorker from './registerServiceWorker';
import 'antd/dist/antd.css';
import { runWithAdal } from 'react-adal';
import { authContext } from './adalConfig';
const DO_NOT_LOGIN = false;
runWithAdal(authContext, () => {
ReactDOM.render(<DashApp />, document.getElementById('root'));
// Hot Module Replacement API
if (module.hot) {
module.hot.accept('./dashApp.js', () => {
const NextApp = require('./dashApp').default;
ReactDOM.render(<NextApp />, document.getElementById('root'));
});
}
},DO_NOT_LOGIN);
registerServiceWorker();
and router.js
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { ConnectedRouter } from 'react-router-redux';
import { connect } from 'react-redux';
import App from './containers/App/App';
import asyncComponent from './helpers/AsyncFunc';
const RestrictedRoute = ({ component: Component, isLoggedIn, ...rest }) => (
<Route
{...rest}
render={props => isLoggedIn
? <Component {...props} />
: <Redirect
to={{
pathname: '/signin',
state: { from: props.location },
}}
/>}
/>
);
const PublicRoutes = ({ history, isLoggedIn }) => {
return (
<ConnectedRouter history={history}>
<div>
<Route
exact
path={'/'}
component={asyncComponent(() => import('./containers/Page/signin'))}
/>
<Route
exact
path={'/signin'}
component={asyncComponent(() => import('./containers/Page/signin'))}
/>
<RestrictedRoute
path="/dashboard"
component={App}
isLoggedIn={isLoggedIn}
/>
</div>
</ConnectedRouter>
);
};
export default connect(state => ({
isLoggedIn: state.Auth.get('idToken') !== null,
}))(PublicRoutes);
The authentication is working fine, but its redirected to a custom login page the template has, I want the redirect to be to /dashboard instead.
Thats the question, should be easy, but new to react routing
Please Give a try. (I'm not 100% sure - I just made a guess from the given code)
router.js
const PublicRoutes = ({ history, isLoggedIn }) => {
return (
<ConnectedRouter history={history}>
<div>
<Route
exact
path={'/'}
render={() => <Redirect to="/dashboard" />}
/>
<Route
exact
path={'/signin'}
component={asyncComponent(() => import('./containers/Page/signin'))}
/>
<RestrictedRoute
path="/dashboard"
component={App}
isLoggedIn={isLoggedIn}
/>
</div>
</ConnectedRouter>
);
};

Categories

Resources