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.
Related
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
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
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.
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
I have a main routes component like this
import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import PrivateRoute from './PrivateRoute'
import AdminPanel from './pages/AdminPanel/AdminPanel'
import SignIn from './pages/SignIn'
import '../assets/stylesheets/styles.scss'
const AppContent = props => <Router>
<div>
<PrivateRoute path='/' component={AdminPanel} />
<Route path='/sign-in' component={SignIn} />
</div>
</Router>
export default AppContent
Here is the PrivateRoute.jsx
import React from 'react'
import { Route, Redirect } from 'react-router-dom'
import { connect } from 'react-redux'
const PrivateRoute = ({ component: Component, currentUser, ...rest }) => (
<Route
{...rest}
render={props =>
currentUser ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: '/sign-in',
state: { from: props.location }
}}
/>
)
}
/>
)
export default connect(({ currentUser }) => ({ currentUser }))(PrivateRoute)
so if currentUser is null it redirects to /sign-in which is perfectly fine. However the problem is if there is currentUser and I go to /sign-in I still get the AdminPanel component.
Here is the currentUser reducer and AdminPanel component in case needed.
import { SIGNIN, SIGNOUT } from '../actions/types'
// so here the default state is null, however if I change it to `{}` which will mean there is a user then I am not able to go to the `/sign-in`
export default (state = null, action) => {
switch (action.type) {
case SIGNIN:
return action.payload
case SIGNOUT:
return null
default:
return state
}
}
And AdminPanel
import React, { Component } from 'react'
import { Route, Switch } from 'react-router-dom'
import Box, { Container } from 'react-layout-components'
import LeftMenu from '../../components/AdminPanel/LeftMenu'
import Header from '../../components/AdminPanel/Header'
import Home from './Home'
import Dashboard from './Dashboard'
import Categories from './Categories'
import Partners from './Partners'
import Users from './Users'
import TeamMembers from './TeamMembers'
import Settings from './Settings'
import Help from './Help'
class AdminPanel extends Component {
render () {
return (
<Box>
<Container minWidth={300}>
<LeftMenu />
</Container>
<Container padding='15px' width='100%'>
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/dashboard' component={Dashboard} />
<Route exact path='/categories' component={Categories} />
<Route exact path='/partners' component={Partners} />
<Route exact path='/users' component={Users} />
<Route exact path='/team-members' component={TeamMembers} />
<Route exact path='/settings' component={Settings} />
<Route exact path='/help' component={Help} />
</Switch>
<Header />
</Container>
</Box>
)
}
}
export default AdminPanel
And SignIn component is just a plain class based component
import React, { Component } from 'react'
class SignIn extends Component {
render () {
return (
<div>SignIn</div>
)
}
}
export default SignIn