I am using react-router-dom 4.2. I have my App.js with Authenticated components inside. This components are created by me and add a little of business logic, create the component via React.createElement, and route them via Route component. Nothing unusual.
The App.js:
// ...
const App = props => (
<BrowserRouter>
<Fragment>
<Switch location={location}>
<Route
exact
path={URLS.ROOT}
render={() => <Redirect to={URLS.DASHBOARD} />}
/>
<Authenticated
{...props}
resource={ResourcesCode.DASHBOARD}
patent={PatentsCode.VIEW}
path={URLS.DASHBOARD}
component={Dashboard}
/>
<Authenticated
{...props}
resource={ResourcesCode.SUBSCRIBE}
patent={PatentsCode.VIEW}
path={URLS.SUBSCRIBE}
component={Subscribe}
/>
</Fragment>
</BrowserRouter>
// ...
Inside of the component Subscribe (mentioned above in the 2nd Authenticated component), I have more routes as you can see below:
// ...
<Route
path={URLS.SUBSCRIBE}
exact
render={() => (
//...
)}
/>
<Route
path={URLS.SUBSCRIBETWO}
exact
render={() => (
//...
)}
/>
// ...
The point is that this routes on the child component (Subscribe) are ignored.
Why are them ignored? How can I solve it?
I really need this routes inside the child component. I don't want to move them to App.js
IMPORTANT EDIT:
The second route is ignored, I realized that the first doesn't. In other words, The Route component with path={URLS.SUBSCRIBE} is working, but the component with path={URLS.SUBSCRIBETWO} is ignored, so here is the problem to solve.
EDIT:
For if you need, the Authenticated component:
// ...
}) => (
<Route
path={path}
exact={exact}
render={route => {
if (!authenticated) {
if (loggingIn) {
return '';
}
return <Redirect to={URLS.LOGIN} />;
}
if (!roleSubReady) {
return '';
}
if (path !== URLS.SUBSCRIBE && user.pendingSubscription) {
if (isLoading) {
return '';
}
return <Redirect to={URLS.SUBSCRIBE} />;
}
if (path === URLS.SUBSCRIBE && !user.pendingSubscription) {
if (isLoading) {
return '';
}
return <Redirect to={URLS.DASHBOARD} />;
}
if (resource && !checkPermission(user, resource, patent)) {
return <NotAuthorized history={route.history} />;
}
return (
<React.Fragment>
<Menu user={user} path={path} isLoading={isLoading} />
<Header show={showHeaderAndFooter} user={user} path={path} />
<MainContent>
{React.createElement(component, {
user,
resource,
...route,
})}
<Footer show={showHeaderAndFooter} />
</MainContent>
</React.Fragment>
);
}}/>
);
Related
Example 1.
routes.js(1st file)
<ProtectedRoutes path={prop.path} component={prop.component} key={key} />
protectedRoute.js(2nd file)
<Route
{...rest}
exact
render={(props) => {
if (verified) return <Component {...props} />;
return (
<Redirect
to={{
pathname: routes.LICENCEKEY,
}}
/>
);
}}
/>
Example 2.
routes.js(1st file)
<ProtectedRoutes exact path={prop.path} component={prop.component} key={key} />
protectedRoute.js(2nd file)
<Route
{...rest}
render={(props) => {
if (verified) return <Component {...props} />;
return (
<Redirect
to={{
pathname: routes.LICENCEKEY,
}}
/>
);
}}
/>
My question is that the first example is not working but the second example is working.
The only difference is that in 1st example I'm writing exact directly inside and in 2nd example I'm passing exact to protectedRoute component and receiving and applying as rest.
Let me know if u understood my question.
EDIT
full protectedRoute.js file
import React from 'react';
import { connect } from 'react-redux';
import { Route, Redirect } from 'react-router-dom';
import routes from 'constants/routes.json';
const ProtectedRoute = ({ verified, component: Component, ...rest }) => {
return (
<Route
exact
{...rest}
render={(props) => {
if (verified) return <Component {...props} />;
return (
<Redirect
to={{
pathname: routes.LICENCEKEY,
}}
/>
);
}}
/>
);
};
const mapStateToProps = (state) => {
return {
verified: state.licencekey.verified,
};
};
export default connect(mapStateToProps)(ProtectedRoute);
I am setting up react router for different link in my project but the problem is I need react router to tell the difference between a user username variable and other paths.
For example:
baseUrl/admin
baseUrl/daniel
React doesnt know the difference. I will have a list of usernames in a db and would return an error if the user doesnt exist then that means the page does not exist.
This is my code:
class App extends Component{
render(){
return (
<Router>
<Route exact path="/" render={props => (
<React.Fragment>
<h1>Hey</h1>
</React.Fragment>
)}
/>
<Route exact path="/admin" render={props => (
<React.Fragment>
<h1>admin</h1>
</React.Fragment>
)}
/>
<Route path="/:user" component={UserComponent}
/>
</Router>
);
}
}
You can use the match.url property to choose which component render, for example:
<Route path="/:user" render={props => {
if(props.match.url === '/admin') {
return (
<React.Fragment>
<h1>Hey</h1>
</React.Fragment>
)
} else return (<UserComponent {...props} />)
}} />
As per me, I have a bit complex routing because I have to handle different domain for different modules. That is why i configured the routes in a following way.
Here it is
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<Root />
</ConnectedRouter>
</Provider>,
MOUNT_NODE,
);
class App extends React.Component {
render() {
return (
<Switch>
<UnauthenticatedRoute path="/auth" component={AsyncLogin} {...this.props} />
<AuthenticatedRoute path="/" component={AsyncHome} {...this.props} />
</Switch>
);
}
}
class Home extends React.Component<propsCheck> {
componentDidMount() {
this.props.getUser();
}
renderRoutes(userRole, roles, userData, props) {
const domain = window.location.hostname; // domain will be like app.abc.com, app.def.com.
switch (domain) {
case GROWTH_URL:
return growthRoutes(userRole, roles, userData, props);
case CONTENT_URL:
return contentRoutes(userRole, roles, userData, props);
default:
return growthRoutes(userRole, roles, userData, props);
}
}
render() {
if (this.props.loading) {
return <Spinner background="none" />;
}
return <Switch>{this.renderRoutes(userRole, roles, userData, this.props)}</Switch>;
}
}
const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
)(Home);
export default withRouter(withConnect);
function NotFoundPage() {
return <div>Not found</div>;
}
export function growthRoutes(userRole, roles, userData, props) {
return (
<Switch>
<Route
exact
path="/"
render={() =>
(!isEmpty(userRole) && userRole.client !== null && isClient(roles)) ||
(!isEmpty(userData) && userData.client !== null && isClient(userData.roles)) ? (
<Redirect to={`${!isEmpty(userRole) ? userRole.client[0].company_slug : userData.company[0]}`} />
) : (
<Redirect to="/clients" />
)
}
/>
<DashboardRoute path="/clients" component={Clients} {...props} />
<DashboardRoute path="/:company/" component={ClientDetail} {...props} />
<DashboardRoute path="/:company/client_detail" component={ClientDetail} {...props} />
<DashboardRoute path="/:company/edit-client" component={Admin(Client)} {...props} />
<DashboardRoute path="/tasks" component={Tasks} {...props} />
<DashboardRoute to="*" component={NotFoundPage} />
</Switch>
);
}
I could not show NotFoundPage this way and could not figure out why it is not working. I have no idea where should i use the snippet <Route path="*" component={NotFoundPage}>. Nowhere it works. Can anyone look at this, please?
The problem is with respect to how you define your routes. since you have a Route /:company/, it will match anything which starts with / like /abc/, /abc/sfsf/fsgs and so on
So what you need to do is to first reorder your Routes, so that all Routes within Switch component work like
<Switch>
<Route
exact
path="/"
render={() =>
(!isEmpty(userRole) && userRole.client !== null && isClient(roles)) ||
(!isEmpty(userData) && userData.client !== null && isClient(userData.roles)) ? (
<Redirect to={`${!isEmpty(userRole) ? userRole.client[0].company_slug : userData.company[0]}`} />
) : (
<Redirect to="/clients" />
)
}
/>
<DashboardRoute path="/clients" component={Clients} {...props} />
<DashboardRoute path="/tasks" component={Tasks} {...props} />
<DashboardRoute path="/:company/client_detail" component={ClientDetail} {...props} />
<DashboardRoute path="/:company/edit-client" component={Admin(Client)} {...props} />
<DashboardRoute exact path="/:company" component={ClientDetail} {...props} />
<DashboardRoute to="*" component={NotFoundPage} />
</Switch>
Now in the above case routes like /abc/ffg/ will show NotFoundPage but still Routes like /abc will still match /:company where company will be abc. So what you need to do in ClientDetail, check if the company is a valid company and return the Correct view and if it isn't you return the NotFoundPage
ClientDetails
render() {
if(!validCompanies.includes(this.props.match.company)) {
return <DashboardRoute to="*" component={NotFoundPage} />
}
// Normal component logic here
}
I got this warning
Warning: You should not use <Route component> and <Route render> in the same route; <Route render> will be ignored
Not sure it's causing the Redirect to fail, but my code below just won't work, it's fine if this.isAuth is true, but not with <Redirect />
https://codesandbox.io/s/5xm202p1j4
render() {
const { Component: component, ...rest } = this.props;
return (
<Route
{...rest}
render={props =>
this.isAuth === false ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login"
}}
/>
)
}
/>
);
}
You have destructured your props incorrectly, it should be const { component: Component, ...rest } = this.props;
The reason that it was giving you the warning was because you tried to destructure Component with uppercase C from props whereas you pass component as props to the authRoute, and not since Component is now present in props the rest params contains the component props which is passed down to the Route.
render() {
const { component: Component, ...rest } = this.props;
return (
<Route
{...rest}
render={props =>
this.isAuth === false ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login"
}}
/>
)
}
/>
);
}
Working sandbox
I'm using React v4.2 for my app, and it seems not to be matching the correct path for the routes:
<Provider store={store}>
<BrowserRouter>
<div>
<Switch>
<Route path="/login" render={(props) => {
if (isAuthenticated()) {
return <Redirect to='/' />;
} else {
return <LoginForm {...props}/>
}
}
} />
<EnsureLoggedInContainer>
<Route exact path="/group" render={(props) => {
debugger;
return <GroupList {...props}/>
}
}/>
<Route exact path="/group/new" render={(props) => {
debugger;
return <GroupList {...props} modal={rr}/>;
}
} />
</EnsureLoggedInContainer>
</Switch>
</div>
</BrowserRouter>
</Provider>
I have some links in the app, on which I click and I redirect the client to new URL:
_onSubmit(values) {
const { history } = this.props;
this.props.createGroup(values, ({status}) => history.push('/group'));
}
I inspect the values of props.history.location.pathname and props.match.path and they don't match. Why is this happening? Why is the correct route not matched?
Update 1
The code for EnsureLoggedInContainer:
class EnsureLoggedInContainer extends Component {
componentDidMount() {
if (!isAuthenticated()) {
dispatch(setRedirectUrl(currentURL))
this.props.history.replace("/login")
}
}
render() {
if (isAuthenticated()) {
return(
<div>
<AppNavBar />
<ComponentsNavBar />
{this.props.children}
</div>
);
} else {
return <noscript />;
}
}
}
export default EnsureLoggedInContainer;
Update 2
I changed the router configuration code to the following:
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<div>
<Switch>
<Route exact path="/login" render={(props) => {
if (isAuthenticated()) {
return <Redirect to='/' />;
} else {
return <LoginForm {...props}/>
}
}
} />
<Route exact path="/register" render={(props) => {
if (isAuthenticated()) {
return <Redirect to='/' />;
} else {
return <RegisterForm {...props}/>
}
}
} />
<EnsureLoggedInContainer>
<Route exact path="/group" component={GroupList} modal={newGroupModal}/>
<Route exact path="/group/new" component={GroupList}/>
<Route component={Home} />
</EnsureLoggedInContainer>
</Switch>
</div>
</BrowserRouter>
</Provider>
, document.querySelector('.container'));
And changed the last line of EnsureLoggedInContainer to :
export default withRouter(EnsureLoggedInContainer);
But still, I get Home always being rendered, and random URLs being matched to unrelated routes (e.g. /group/new)
As per the documentation:
A match object contains information about how a matched
the URL. match objects contain the following properties:
params - (object) Key/value pairs parsed from the URL corresponding to the dynamic segments of the path
isExact - (boolean) true if the entire URL was matched (no trailing characters)
path - (string) The path pattern used to match. Useful for building nested <Route>s
url - (string) The matched portion of the URL. Useful for building nested <Link>s
while
Locations represent where the app is now, where you want it to go, or
even where it was.
so if you are on say /group/new, location.pathname will give you /group/new whereas match.path will give your the Route path defined for the component in which you log it if it is matched
I finally managed to resolve the issue by using the private route pattern.