lazy loading with react-router-dom private route - javascript

Problem:
I have created a react application there I am using the private route from react-router-dom and using Lazy loading. This is my index.js file.
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { PrivateRoute } from "./components/PrivateRoute/PrivateRoute";
import SignIn from "./components/SignIn/SignIn";
import AdminLayout from "./components/Admin/AdminLayout/AdminLayout";
import OfficerLayout from "./components/Officer/OfficerLayout/OfficerLayout";
import * as serviceWorker from "./serviceWorker";
import { Provider } from "react-redux";
import { createLogicMiddleware } from "redux-logic";
import { createStore, applyMiddleware, compose } from "redux";
import { Route, Switch, BrowserRouter } from "react-router-dom";
import "primereact/resources/themes/nova-light/theme.css";
import "primereact/resources/primereact.min.css";
import "primeicons/primeicons.css";
import reducers from "./reducers";
import services from "./services";
const logicMiddleware = createLogicMiddleware(services, {});
const middleware = applyMiddleware(logicMiddleware);
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const enhancer = composeEnhancers(middleware);
let store = createStore(reducers, enhancer);
// let store = applyMiddleware(promiseMiddeleware)(createStore);
ReactDOM.render(
// <Provider
// store={store(
// reducers,
// window.__REDUX_DEVTOOLS_EXTENSION__ &&
// window.__REDUX_DEVTOOLS_EXTENSION__()
// )}
// >
<Provider store={store}>
<BrowserRouter>
<Switch>
<Route exact path="/" component={SignIn} />
<Route path="/signin" component={SignIn} />
<PrivateRoute
exact
path="/admin"
name="Admin"
component={AdminLayout}
></PrivateRoute>
<PrivateRoute
exact
path="/officer"
name="Officer"
component={OfficerLayout}
></PrivateRoute>
</Switch>
</BrowserRouter>
</Provider>,
document.getElementById("root")
);
This is my private route component.
import React, { Component } from "react";
import { Redirect, Route } from "react-router-dom";
export const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
localStorage.getItem("jwt") ? (
<Component {...props}></Component>
) : (
<Redirect
to={{
pathname: "/",
state: { from: props.location }
}}
></Redirect>
)
}
></Route>
);
This is my officer layout component.
import React, { Component, Suspense } from "react";
import { Redirect, Route, Switch } from "react-router-dom";
import routes from "../officer_routes";
import "./OfficerLayout.css";
import { Container, Card, Row, Col } from "reactstrap";
const OfficerHeader = React.lazy(() =>
import("../OfficerHeader/OfficerHeader")
);
class OfficerLayout extends Component {
loading = () => (
<div className="animated fadeIn pt-1 text-center">Loading...</div>
);
componentDidMount() {
document.body.classList.add("officer-layout-background");
}
componentWillUnmount() {
document.body.classList.replace("background-color", "no-color");
}
render() {
return (
<div>
<Container fluid>
<div className="app-panel">
<div className="police-app-header">
<Suspense fallback={this.loading()}>
<OfficerHeader />
</Suspense>
</div>
<Row>
<Col lg="1" />
<Col lg="10">
<Card className="police-app-panel-card">
{" "}
<Suspense fallback={this.loading()}>
<Switch>
{routes.map((route, idx) => {
return route.component ? (
<Route
key={idx}
path={route.path}
exact={route.exact}
name={route.name}
render={props => <route.component {...props} />}
/>
) : null;
})}
<Redirect from="/officer" to="/officer/doFine" />
</Switch>
</Suspense>{" "}
</Card>
</Col>
<Col lg="1" />
</Row>
</div>
</Container>
{/* <footer className="police-app-footer">iiii</footer> */}
</div>
);
}
}
export default OfficerLayout;
This is my officer routes.
import React from "react";
const OfficerPanel = React.lazy(() => import("./OfficerPanel/OfficerPanel"));
const EnforcedFines = React.lazy(() => import("./EnforcedFines/EnforcedFines"));
const DriverList = React.lazy(() => import("./DriverList/DriverList"));
const FineSearchSingle = React.lazy(() =>
import("./FineSearchSingle/FineSearchSingle")
);
const DriverDetails = React.lazy(() => import("./DriverDetails/DriverDetails"))
const Profile = React.lazy(()=> import("./Profile/Profile"))
const ChangePassword = React.lazy(() => import("./Profile/ChangePassword/Changepassword"))
const routes = [
{ path: "/officer/findDriver", name: "findDriver", component: DriverList },
{
path: "/officer/singleFine/:id",
name: "SingleFine",
component: FineSearchSingle
},
{
path: "/officer/driverDetails/:id",
name: "Driver Details",
component: DriverDetails
},
{
path: "/officer/profile",
name: "Profile",
component: Profile
},
{
path: "/officer/changePassword",
name: "Change Password",
component: ChangePassword
}
];
export default routes;
The private route is working correctly It means when the user is not login to system it redirects to the login page. But routes like /officer/findDriver does not redirect correctly. Can someone help me to solve this issue? Thank you

Related

React-query / React-router dom issue

If I am using react-query and react-router, I can see the network requests happening when navigating, however the new data is not reflected in the view/page? Am I doing something wrong? I am doing lazy loading:
App.tsx
import React, { Suspense } from 'react';
import Loader from '../loader/Loader';
const App: React.FunctionComponent = () => {
const Routing = React.lazy(() => import('../routing/Routing'));
return (
<>
<Suspense fallback={<Loader />}>
<DataProvider>
<Routing />
</DataProvider>
</Suspense>
</>
);
}
export default App;
router component:
import React from 'react';
import { BrowserRouter, Route, Switch} from 'react-router-dom';
import Header from '../header/Header';
import Component1 from './Component1';
import Compontent2 from './Component2';
import { QueryClient, QueryClientProvider } from 'react-query';
import { RouteComponentProps } from "react-router-dom";
const queryClient = new QueryClient()
const Routing: React.FunctionComponent = () => {
return (
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<Header />
<div className="App">
<Switch>
<Route exact path="/" component={Component1} />
<Route path="/details/:id" render={(props: RouteComponentProps<any>) => <Component2 {...props}/>} />
</Switch>
</div>
</BrowserRouter>
</QueryClientProvider>
)
}
export default Routing;
Component 1
const fetchAll = async () => {
const res = await fetch('https:xxxxxx/xxxxx');
return res.json();
};
const { data: result, status } = useQuery('info', fetchAll, {
staleTime: 0,
});
component 2 is using the same approach the react-query stuff is not in useEffect of anything?
How can I get the view to re-render on navigation and show the new data.
try and move the call to React.lazy outside of the fallback component?
const Routing = React.lazy(() => import('../routing/Routing'));
const App: React.FunctionComponent = () => {
return (
<>
<Suspense fallback={<Loader />}>
<DataProvider>
<Routing />
</DataProvider>
</Suspense>
</>
);
}
export default App;
That's how they're using it in the official React docs here:
https://reactjs.org/docs/code-splitting.html

React-router-dom Protected Routes are not working

Protected Routes.js:
In protected routes you can see I'm using directly false in if statement but I'm still able to see that page why?
import React from 'react';
import { Route } from 'react-router-dom';
// import Auth from './User/Auth';
import Error401 from './Error/401';
// create a component for protected route
console.log('Routes.js');
export const ProtectedRoute = ({ element: Element, ...rest }) => {
console.log("Function Called")
return (
<Route {...rest} render={props => {
if(false){
return <Element {...props} />
}else{
return <Error401 />
}
}
} />
)
}
App.js:
This is app.js where I'm using protected routes component
import './App.css';
import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { Layout } from 'antd';
import { MoneyCollectOutlined } from '#ant-design/icons';
import Login from './Components/User/Login';
import Signup from './Components/User/Signup';
import {ProtectedRoute} from './Components/Routes';
import Error404 from './Components/Error/404';
function App() {
return (
<BrowserRouter>
<Layout style={{minHeight:"100vh"}}>
<Layout.Header>
<h1 style={{color:"white"}} align="center"> <MoneyCollectOutlined/>MoneyG</h1>
</Layout.Header>
<Layout.Content style={{minHeight:"100%"}}>
<Routes>
<ProtectedRoute exact path="/register" element={<Signup/>} />
<ProtectedRoute exact path="/login" element={<Login/>} />
<Route path="*" element={<Error404/>} />
</Routes>
</Layout.Content>
</Layout>
</BrowserRouter>
);
}
export default App;
First, <Routes> elements should only have <Route> elements as children. You should move your protection logic down a layer.
Secondly, the render prop doesn't exist anymore in V6. It was replaced in favor of element. See doc.
Here is how you might tackle it:
<Routes>
<Route exact path="/register" element={(
<ProtectedRoute>
<Signup/>
</ProtectedRoute>
)} />
<Route exact path="/login" element={(
<ProtectedRoute>
<Login/>
</ProtectedRoute>
)} />
<Route path="*" element={<Error404/>} />
</Routes>
And:
const ProtectedRoute = () => {
if (condition) { return <Error401 />; } // You might as well use Navigate here
return children;
};
you can use createContext & useContext
//store/AuthApi.jsx
import { createContext } from "react";
const AuthApi = createContext();
export default AuthApi;
Then define the context app.jsx
import React, from 'react'
import { AllRoutes } from 'routes/Routes';
import { BrowserRouter as Router } from "react-router-dom";
import AuthApi from 'store/AuthApi';
const App = () => {
const [user, setUser] = useState(false);
useEffect(() => {
// you can get user from localStorage or Cookie(js-cookie npm)
//then you can change user state true or false
}, [])
return (
<>
<AuthApi.Provider value={{ user, setUser }}>
<Router>
<AllRoutes />
</Router>
</AuthApi.Provider>
<Toast />
</>
)
}
export default App
then see AllRoutes
//routes/Routes
import React, { useContext } from "react";
import { Routes, Route } from "react-router-dom";
import { SignIn, SignUp, Dashboard } from "pages";
import AuthApi from "store/AuthApi";
export const ProtectedRouting = () => {
return (
<Routes >
<Route path='/' exact element={<Dashboard />} />
// add more protected routes
</Routes>
)
}
export const AuthRouting = () => {
return (
<Routes >
<Route exact={true} path='/sign-in' element={<SignIn />} />
<Route exact={true} path='/sign-up' element={<SignUp />} />
</Routes>
)
}
export const AllRoutes = ()=> {
const context = useContext(AuthApi);
console.log(context.user)
return (
context.user ?
<ProtectedRouting />
: <AuthRouting />
)
}
pages/SignIn.jsx
import React,{ useContext } from 'react';
import AuthApi from "store/AuthApi";
const SignIn = () => {
const context = useContext(AuthApi);
const signInSubmit =(e)=> {
e.preventDefault();
//post request to signin
// if login is successfull then save user or token in cookie or localStorage or something
context?.setUser(true);
//...
}
return (
//signin code here
<form onSubmit={signInSubmit}>
///input here
</form>
)
}
export default SignIn

React not rendering correctly layout after redirect

I have an app that uses styled-components and material-ui. I have private routes configurated, based on the user role and if the user is not authenticated I should return him to login page. But when that happen the layout do no load.
App.tsx
import React from 'react';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core';
import { Provider } from 'react-redux';
import { ToastContainer } from 'react-toastify';
import { PersistGate } from 'redux-persist/integration/react';
import { ThemeProvider as StyledThemeProvider } from 'styled-components';
import Routes from './routes';
import { configureStore } from './store/configure-store';
import GlobalStyle from './styles/globalTheme';
import defaultTheme from './styles/theme/default.theme';
const { store, persistor } = configureStore();
const theme = createMuiTheme({});
function App() {
return (
<Provider store={store}>
<MuiThemeProvider theme={theme}>
<StyledThemeProvider theme={defaultTheme}>
<PersistGate loading={null} persistor={persistor}>
<GlobalStyle />
<Routes />
<ToastContainer />
</PersistGate>
</StyledThemeProvider>
</MuiThemeProvider>
</Provider>
);
}
export default App;
routes.tsx
import React, { memo } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import NotFound from '../components/NotFound';
import Dashboard from '../pages/Dashboard';
import Login from '../pages/Login';
import Register from '../pages/Register';
import { ProtectedRoute } from './permissions-route';
function Routes() {
return (
<BrowserRouter>
<Switch>
<ProtectedRoute
path="/dashboard"
component={Dashboard}
requiredRole={['USER']}
/>
<Route exact path="/" component={Login} />
<Route exact path="/login" component={Login} />
<Route exact path="/cadastro" component={Register} />
<Route exact path="/error" component={NotFound} />
</Switch>
</BrowserRouter>
);
}
export default memo(Routes);
permission-route.tsx
import React from 'react';
import { useSelector } from 'react-redux';
import { Route, Redirect } from 'react-router-dom';
import { RootState } from '../store/configure-store';
interface Teste {
path: any;
component: any;
render?: any;
requiredRole: any;
}
export const ProtectedRoute = ({
path,
component: Component,
render,
requiredRole,
...rest
}: Teste) => {
const userRole = useSelector((state: RootState) => state.user.role);
return (
<Route
path={path}
{...rest}
render={(props) => {
if (requiredRole.includes(userRole)) {
return Component ? <Component {...props} /> : render(props);
}
return <Redirect to="/login" />;
}}
/>
);
};
export default ProtectedRoute;
What happens after redirect occurs
Instead of returning the <Redirect /> component, try window.location.href = "/login";
return (
<Route
path={path}
{...rest}
render={(props) => {
if (requiredRole.includes(userRole)) {
return Component ? <Component {...props} /> : render(props);
}
window.location.href = "/login";
}}
/>
);
This should refresh the page and make it get the proper stylings again
I had a similar issue using MUI and Router.
I found that I was importing different versions of MUI: #mui and #material-ui after replacing #material-ui components and uninstalling it, the issue was resolved.
Most likely this is an issue with conflicting styling.
I suggest checking browser DevTools to see what files/classes are overriding these styles after redirect.

"react-router-dom " router component does not work

I want to use history explicitly. So I know about BrowserRoute but I want to use Route and use history as its property. when I run the program I get this page.enter image description here
This is my AppRouter.js
import React from "react";
import { Router, Route, Switch } from "react-router-dom";
import { createBrowserHistory } from "history";
import LoginPage from "../components/LoginPage";
import ExpenseDashboardPage from "../components/ExpenseDashboardPage";
import AddExpensePage from "../components/AddExpensePage";
import EditExpensePage from "../components/EditExpensePage";
import NotFoundPage from "../components/NotFoundPage";
import PublicRoute from "./PublicRoute";
import PrivateRoute from "./PrivateRoute";
export const history = createBrowserHistory();
const AppRouter = () => (
<Router history={history}>
<div>
<Switch>
<PublicRoute path="/" component={LoginPage} exact={true} />
<PrivateRoute path="/dashboard" component={ExpenseDashboardPage} />
<PrivateRoute path="/create" component={AddExpensePage} />
<PrivateRoute path="/edit/:id" component={EditExpensePage} />
<Route component={NotFoundPage} />
</Switch>
</div>
</Router>
);
export default AppRouter;
LoginPage.js
import React from "react";
import { connect } from "react-redux";
import { startLogin } from "../actions/auth";
export const LoginPage = ({ startLogin }) => (
<div>
<button onClick={startLogin}>Login with Google</button>
<button>Amir Hossein Jobeiri</button>
</div>
);
const mapDispatchToProps = (dispatch) => ({
startLogin: () => dispatch(startLogin()),
});
export default connect(undefined, mapDispatchToProps)(LoginPage);
PublicRoute.js
import React from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";
export const PublicRoute = ({
isAuthenticated,
component: Component,
...rest
}) => (
<Route
{...rest}
component={(props) =>
isAuthenticated ? <Redirect to="/dashboard" /> : <Component {...props} />
}
/>
);
const mapStateToProps = (state) => ({
isAuthenticated: !!state.auth.uid,
});
export default connect(mapStateToProps)(PublicRoute);
PrivateRoute
import React from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";
import Header from "../components/Header";
export const PrivateRoute = ({
isAuthenticated,
component: Component,
...rest
}) => (
<Route
{...rest}
component={(props) =>
isAuthenticated ? (
<div>
<Header />
<Component {...props} />
</div>
) : (
<Redirect to="/" />
)
}
/>
);
const mapStateToProps = (state) => ({
isAuthenticated: !!state.auth.uid,
});
export default connect(mapStateToProps)(PrivateRoute);
I searched a lot about this problem but I do not get the right answer. Please help if you can.
I changed my "history" version to "4.7.2". It works properly.

Redirect to login page using react-router v4

I'm new in React.
I want to create an amdin app that redirects to login by default (the main menu is hidden). Using react-router v4, based on this sample the menu is already shown by default. What is the best practice to do this? Here is my current code
in index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {
BrowserRouter,
Route,
Switch
} from 'react-router-dom';
import createHistory from 'history/createBrowserHistory'
import registerServiceWorker from './registerServiceWorker';
import './index.css';
import App from './App';
import Login from './modules/Login';
ReactDOM.render((
<BrowserRouter history={createHistory}>
<Switch>
<Route path="/login" component={Login} />
<Route path="/" component={App} />
</Switch>
</BrowserRouter>
), document.getElementById('root'));
registerServiceWorker();
in app.js
import React from 'react'
import {
BrowserRouter,
Link,
Switch,
Route,
Redirect
} from 'react-router-dom'
import { routes } from './router-config'
import Login from './modules/Login'
import fakeAuth from './Auth'
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest} render={props => (
fakeAuth.isAuthenticated ? (
<Component {...props}/>
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location }
}} />
)
)} />
);
const App = () => (
<BrowserRouter>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/protected">Protected</Link></li>
</ul>
<Switch>
{routes.map((route,index) => (
<PrivateRoute key={index} path={route.pattern} component={route.component} exact={route.exactly} />
))}
</Switch>
</div>
</BrowserRouter>
)
export default App
in login.js
import React, { Component } from 'react'
import {
Redirect
} from 'react-router-dom'
import fakeAuth from '../Auth'
import App from '../App'
class Login extends Component {
state = {
redirectToReferrer: false
}
login = () => {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true })
})
}
render() {
const { from } = this.props.location.state || { from: { pathname: '/' } }
const { redirectToReferrer } = this.state
if (fakeAuth.isAuthenticated) {
return (
<Redirect to={"/"}/>
)
}
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={this.login}>Log in</button>
</div>
)
}
}
export default Login;
i able to click login and redirect to App, but when i refresh the page when in App, the url is changed to /login but the display is still in the App. Please help. Thanks a lot
I solved this problem by creating component and load it in App
here is my code in app.js:
import React from 'react'
import {
BrowserRouter,
Link,
Switch,
Route,
Redirect,
withRouter
} from 'react-router-dom'
import { routes } from './router-config'
import Login from './modules/Login'
import fakeAuth from './Auth'
import About from './modules/About'
import Protected from './modules/Protected'
import './App.css';
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
fakeAuth.isAuthenticated ? (
<Component {...props}/>
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location }
}}/>
)
)}/>
)
const AuthButton = withRouter(({ history }) => (
fakeAuth.isAuthenticated ? (
<div>
Welcome! <button onClick={() => {
fakeAuth.signout(() => history.push('/login'))
}}>Sign out</button>
<ul>
<li><Link to="/about">About</Link></li>
<li><Link to="/protected">Protected Page</Link></li>
</ul>
</div>
) : (
<Redirect to={{
pathname: '/login'
}}/>
)
))
const App = () => (
<BrowserRouter>
<div>
<AuthButton/>
<Route path="/login" component={Login}/>
<PrivateRoute path="/about" component={About}/>
<PrivateRoute path="/protected" component={Protected}/>
</div>
</BrowserRouter>
)
export default App
Login.js
import React, { Component } from 'react'
import {
Redirect
} from 'react-router-dom'
import fakeAuth from '../Auth'
class Login extends React.Component {
state = {
redirectToReferrer: false
}
login = () => {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true })
})
}
render() {
const { from } = this.props.location.state || { from: { pathname: '/' } }
const { redirectToReferrer } = this.state
if (redirectToReferrer) {
return (
<Redirect to={from}/>
)
}
return (
<div>
<p>Please Login</p>
<div>
</div>
<Button onClick={this.login} type="primary">Log in</Button>
</div>
)
}
}
export default Login;
I don't really know if it's the best way to do this.

Categories