I cannot configure the route correctly. Shop component not renders
Component structure:
index.tsx:
const Index: FC = () => {
const SignIn = props => (
<LazyLoad
component={React.lazy(() => import('./modules/auth/containers/SignIn'))}
{...props}
/>
)
return (
<Provider store={store}>
<EmotionThemeProvider>
<ThemeProvider theme={theme}>
<Router>
<Switch>
<PrivateRoute exact path='/' component={Main} />
<Route path='/signin' component={SignIn} />
</Switch>
</Router>
</ThemeProvider>
</EmotionThemeProvider>
</Provider>
)
}
PrivateRoute.tsx:
export const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
localStorage.getItem('accessToken') ? (
<Component {...props} />
) : (
<Redirect
to={{ pathname: '/signin', state: { from: props.location } }}
/>
)
}
/>
)
Main.tsx:
const Main: FC = () => (
<App>
<Switch>
{routes.map(route => (
<Route
key={route.path}
path={route.path}
exact={route.exact}
component={route.component}
/>
))}
</Switch>
</App>
)
Shop.tsx:
const Shop = () => {
const [, setCollapsed] = useContext(SidebarContext)
useEffect(() => {
setCollapsed(true)
}, [])
return <div>Shop</div>
}
routes.ts:
export const routes = [
{
path: '/shop',
key: 'market',
component: props => (
<LazyLoad
component={React.lazy(() => import('./modules/shop/pages/Shop'))}
{...props}
/>
),
},
{
path: '/',
exact: true,
component: () => <div>main</div>,
skip: true,
},
]
When I logged in, the main page opens, but after when I go to the shop, it no longer renders.
Where is my mistake? I have tried to remove 'exact' from PrivateRoute, then shop opens, but I got this error:
Uncaught Error: Maximum update depth exceeded. This can happen when a
component repeatedly calls setState inside componentWillUpdate or
componentDidUpdate. React limits the number of nested updates to
prevent infinite loops
Related
Routes:
const issueSubmissionModal = () => IssueModal({ isVisible: true })
const renderHeader = () => <AppHeader userData={userData}><AppNavbar/></AppHeader>
const renderHome = () => <AppTemplate userData={userData} />
const Routes = userData ? [
{
key: "home",
path: "/",
component: renderHome(),
includeProfile: null,
includeAppheader: renderHeader()
},
{
key: "issueSubmission",
path: "/issueSubmission",
component: issueSubmissionModal(),
includeProfile: renderHome(),
includeAppheader: renderHeader()
},
] : []
const renderRoutes = () => <Switch>
{Routes.map(route => (
<Route exact path={route.path} key={route.key}>
{route.includeProfile && route.includeProfile}
{route.component}
</Route>
))}
</Switch>
return renderRoutes();
App.tsx:
function App() {
const { data: userData, isLoading, isError, error } = useAppUser();
return (
<div className="App">
<Router>
{userData ?
<Routes userData={userData} />
: null}
</Router>
</div>
);
}
export default App;
Problem? No matter what I do, all routes render as soon as the page launches. I simplified to two routes here, but the actual app has several.
It seems to me Switch isn't working. I don't know why.
I can go back to just having all the routes written out manually in App.tsx, but that's gross and inefficient for what we're trying to do.
Any React gurus got an idea?
Probably you are missing to include component tag within <Route />
{
Routes.map(route => (
<Route exact path={route.path} key={route.key} component=
{route.includeProfile ? route.component} />
)
)
}
I'm using module federation from webpack and my core app contained all routes to the rest of the app. What works fine is that inside the Switch, I just had each AuthRoute or Route manually rather than using the map. Suspense was wrapping the Switch so that the direct children are just Route. I'm now doing some splitting but I can't get it to work. Any ideas?
my routes are set up as so (and localRoutes is at the bottom):
const routes = [
...localRoutes,
// ...remoteRoutes
];
Inside my BrowserRouter I map routes based on whether the user is authorised for that route or not. I suspect the problem is here but don't understand why Route or AuthRoute that returns a Route won't work since it's a directly under the Switch.
<Switch>
{routes.map((route) => {
console.log(route)
route.auth ?
<AuthRoute
key={route.path}
path={route.path}
component={route.component}
exact={route.exact}
requiredRoles={route.requiredRoles}
/>
:
<Route
key={route.path}
path={route.path}
component={route.component}
exact={route.exact}
/>
})}
<Redirect to='/login' />
</Switch>
where authRoute:
const AuthRoute = ({ Component, path, exact, requiredRoles }) => {
const isLoggedIn = true // or false
const roles = ['admin', 'sth_else']
const userHasRequiredRole = intersection(requiredRoles, roles).length > 0
const message = userHasRequiredRole ? 'Please log in to view this page' : "You can't be here!"
return (
<Route
exact={exact}
path={path}
render={(props) =>
isLoggedIn && userHasRequiredRole
? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: userHasRequiredRole ?
'/login' :
'/modules',
state: {
message,
requestedPath: path
}
}}
/>
)
}
/>
);
};
export default AuthRoute;
and example routes:
const AboutPage = lazy(() => import('core/AboutPage'))
const LoginPage = lazy(() => import('core/LoginPage'))
const MyModules = lazy(() => import('core/MyModules'))
const routes = [
{
auth: true,
path: "/modules",
component: MyModules,
exact: false,
requiredRoles: [
String(UserRoles.Administrator),
String(UserRoles.AnotherRole),
String(UserRoles.Another)
]
},
{
auth: false,
path: "/about",
component: AboutPage,
exact: false,
}
];
If you are doing lazyLoad specify component as a function inside <Route>.
In your case try the following:
<Route
key={route.path}
path={route.path}
component={(props) => (<route.component {...props} />)}
exact={route.exact}
/>
I think the above code should work if you add a return before ternary operator
retur route.auth ?...
I am having similar structure in my app
<Suspense fallback={<Spinner />}>
<Switch>
<Refresh path="refresh" />
{routes.map((route) => {
return <PrivateRoute key={route.path} {...route} user={user} />;
})}
{routes.length && user && <Route component={NotFound} />}
</Switch>
</Suspense>
I'm looking for a way to do some route protection with react-router-4. Looking at an example in the documentation, they create a Component which is rendered like this:
<PrivateRoute path="/protected" component={Protected} />
and the privateRoute component:
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
fakeAuth.isAuthenticated ? (
<Component {...props}/>
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location }
}}/>
)
)}/>
)
I don't really understand why I should want to pass the "Protected" component as a property and then have to take care of spreading all the ...props and ...rest.
Before I was reading this doc (and other example), I created the following code, which just nest the routes in another component which takes care of the authentication part.
Because my example (which seems to work perfectly well), looks way more simplistic, I must be missing something.
Are there any downsides on this approach?
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
import Nav from './Nav';
// dummy
const Auth = {
isAuthenticated: () => { return true; }
}
const Home = () => <h1>Home</h1>
const SignIn = () => <h1>SignIn</h1>
const About = () => <h1>About</h1>
class PrivateOne extends Component {
render() {
console.log(this.props);
return <h1>Private</h1>
}
}
const PrivateTwo = () => <h1>PrivateTwo</h1>
const PrivateThree = () => <h1>PrivateThree</h1>
const NotFound = () => <h1>404</h1>
const Private = ({isAuthenticated, children}) => {
return(
isAuthenticated ? (
<div>
<h1>Private</h1>
{children}
</div>
) : (
<Redirect to={{
pathname: '/sign_in',
}}/>
)
)
}
const App = () =>
<div>
<Router>
<div>
<Nav />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/sign_in" component={SignIn} />
<Private isAuthenticated={Auth.isAuthenticated()}> {/* or some state later on */}
<Route path="/private1" component={PrivateOne} />
<Route path="/private2" component={PrivateTwo} />
<Route path="/private3" component={PrivateThree} />
</Private>
<Route component={NotFound} />
</Switch>
</div>
</Router>
</div>
export default App;
I have a simple route.js file
const PrivateRoute = ({ component, ...rest }) => {
const isAuthed = localStorage.getItem('Authorization')
return (
<Route {...rest} exact
render = {(props) => (
isAuthed ? (
<div>
{React.createElement(component, props)}
</div>
) :
(
<Redirect
to={{
pathname: '/login',
state: { from: props.location }
}}
/>
)
)}
/>
)
}
class App extends Component {
componentWillMount() {
if (localStorage.getItem('Authorization')) {
history.push(`${history.location.pathname}`)
}
}
render() {
return (
<Router history={history}>
<div className="App-pageContainer">
<Route exact path="/" render={() => <Redirect to="/login" />} />
<Route path={'/login'} component={Login} />
<PrivateRoute path={'/dashboard'} component={Dashboard} />
</div>
</Router>
)
}
}
export default App
What I need is to put condition if the user has a key in localStorage(Authentication) then I want to redirect it to /dashboard if it doesn't contain Authentication in localStorage then I want to redirect it to /login.
I am totally stuck with this from past few days. Please help!!!
I think this kind of questions is too broad for an answer.
However, you could follow this amazing post to be able to implement that functionality.
Protected routes and authentication with React Router v4
This is what you got after finishing
import React from 'react'
import {
BrowserRouter as Router,
Route,
Link,
Redirect,
withRouter
} from 'react-router-dom'
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true
setTimeout(cb, 100)
},
signout(cb) {
this.isAuthenticated = false
setTimeout(cb, 100)
}
}
const Public = () => <h3>Public</h3>
const Protected = () => <h3>Protected</h3>
class Login extends React.Component {
render() {
return (
<div>
Login
</div>
)
}
}
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
fakeAuth.isAuthenticated === true
? <Component {...props} />
: <Redirect to='/login' />
)} />
)
export default function AuthExample () {
return (
<Router>
<div>
<ul>
<li><Link to="/public">Public Page</Link></li>
<li><Link to="/protected">Protected Page</Link></li>
</ul>
<Route path="/public" component={Public}/>
<Route path="/login" component={Login}/>
<PrivateRoute path='/protected' component={Protected} />
</div>
</Router>
)
}
The title says it all.
When I type a url manually in a browser or try to refresh, it always goes back to root, is there any thing wrong? because I'm not getting it.
When I refresh or type url manually and I'm logged in it goes to the dashboard, and when I'm not logged in, it goes to login page.
Here is my code
import { Switch, Route, Redirect, Router } from 'react-router-dom';
<Provider store={this.props.store}>
<IntlProvider locale={this.props.locale} messages={this.props.localeData}>
<LocaleProvider locale={enUS}>
<Router history={history}>
<Switch>
<Route exact path="/" render={() => (<Redirect to="/dashboard" />)} />
<PrivateRoute path="/dashboard" component={App} locale={this.props.locale} redirectTo="/login" />
<PropsRoute path="/login" component={Login} />
<PropsRoute path="/reset-password" component={ResetPassword} />
<PropsRoute path="/loader" component={Loader} spinning={true} fullScreen={true} />
<Route component={NoMatch} />
</Switch>
</Router>
</LocaleProvider>
</IntlProvider>
</Provider>
this is my props route
const renderMergedProps = (component, ...rest) => {
const finalProps = Object.assign({}, ...rest);
return (
React.createElement(component, finalProps)
);
};
const PropsRoute = ({ component, ...rest }) => {
return (
<Route {...rest} render={routeProps => {
return renderMergedProps(component, routeProps, rest);
}} />
);
};
and my private route
const PrivateRoute = ({ user, component, redirectTo, ...rest }) => {
return (
<Route {...rest} render={routeProps => {
return user.logged ? (
renderMergedProps(component, routeProps, rest)
) : (
<Redirect to={{
pathname: redirectTo,
state: { from: routeProps.location }
}} />
);
}} />
);
};
Note: I'm also using Redux and Redux Saga.
const PrivateRoute = ({ user, component, redirectTo, ...rest }) => {
return (
<Route {...rest} render={routeProps => {
return user.logged ? (
renderMergedProps(component, routeProps, rest)
) : (
<Redirect to={{
pathname: redirectTo,
state: { from: routeProps.location }
}} />
);
}} />
);
};
Would say you should check if your user.logged hasn't change, it may be the case if your re-rendering the app when you manually change the url.
You could use localstorage to check if user is indeed logged or not.