Using codes below, it only linked to ListComponent when i want to linked to DetailsComponent. If change
Details: route('data/tower/list/item'),
to
Details: route('data/tower/item'),
it can link to DetailsComponent.I don't why and how to fix it ?
const EnumRouter = {
...
List: route('data/tower/list'),
Details: route('data/tower/list/item'),
};
<Switch>
...
//ListComponent
<MainLayout path={EnumRouter.List} component={List} />
//DetailsComponent
<MainLayout path={EnumRouter.Details} component={Details} />
...
</Switch>
That happens because
You are making use of Switch Component, which renders the first route that matches, which is ofCourse correct thing to do.
You have your List Route as 'data/tower/list' and your Details Route as 'data/tower/list/item', however Router doesn't look for a complete match, In your case 'data/tower/list' matches(although not completely but with the initial part) the Details Route and hence even when you try to Route to Details, it routes to List component.
The solution is to make use of the exact attribute for the Route.
From the Documentation:
exact: bool
When true, will only match if the path matches the location.pathname exactly.
**path** **location.pathname** **exact** **matches?**
/one /one/two true no
/one /one/two false yes
Change the code to
<Switch>
...
//ListComponent
<MainLayout exact path={EnumRouter.List} component={List} />
//DetailsComponent
<MainLayout path={EnumRouter.Details} component={Details} />
...
</Switch>
Related
I am using the same component for three different routes:
<Router>
<Home path="/" />
<Home path="/home" />
</Router>
Is there anyway to combine it, to be like:
<Router>
<Home path=["/home", "/"] />
</Router>
For Reach Router: (https://reach.tech/router/example/)
With the exact sample shown, the only way I can see how to do this(on a single line) is with a wildcard.
To find a way to reproduce this without side effects, we would need to see the entire nav menu.
<Router>
<Home path="/*" />
<Chicken path="chicken">
</Router>
...
const Home = props => {
let urlPath = props["*"]
// URL: "/home"
// urlPath === "home"
// URL/: "/"
// urlPath ===""
}
You could continue with other paths below Home and the router would allow them to process.
Check out the the example using a wildcard and reach router on codesandbox, I wrote!
Note: This is a catch-all, but without parsing a parameter is the only single line solution I saw.
Some DrawBacks include Home rendering instead of '404', etc.
//This could be resolved with an if statement in your render
//It will not produce the intended URL either for /home, and I have not looked into that since it is not part of the question.. but if it matched props[*] I'm sure you could redirect or something.
You can read more about the Route Component for Reach Router.
https://reach.tech/router/api/RouteComponent
I wasn't happy with the wildcard solution from the documentation and #cullen-bond because I had to map many other paths and came up with this solution:
<Router>
{["/home", "/", "/other", "/a-lot-more"].map(page => <Home path={page} />)}
</Router>
Example: https://codesandbox.io/s/reach-router-starter-v1-forked-6f44c?file=/src/index.js
Depending on the situation you're dealing with, <Redirect /> could also make the work.
<Router>
<Redirect from="/" path="/home" noThrow />
<Home path="/home" />
</Router>
You can use a single component for mutiple paths, by using a array of routes.
code example :
import sampleComponent from './sampleComponent'; // single component for mutiple routes
<Router>
<Switch>
{["/pathname_1", "/pathname_2", "/pathname_3", "/pathname_4", "/pathname_5", "/pathname_6"].map(pathname => (<Route exact path={pathname} component={sampleComponent} />) )}
<Switch>
<Router>
I have a case in react router v4:
<Route path="/:time?/:zone?" component={TimeComponent} />
However I don't want to render that TimeComponent if route is:
/user/userId123
As you can see above, the path matches user as :time and userId123 as :zone.
My goal: I don't want path to match if route contains /user.
I know I can use regex, but how to specify that I dont want to match the path if contains /user?
Looking for help
You can use switch from react router dom like this:
<Switch>
<Route path="/user/:userId?" component={UserComponent} />
<Route path="/:time?/:zone?" component={TimeComponent} />
</Switch>
With this it will match only one route at a time and you 2 param route is at the end. So using this approach you can catch /user route before time component route.
Please try this.
I encountered a problem with React Router v4 Switch component. I'm quite surprised that i couldn't find a relevant thread for this problem. A common Switch will look like this:
<Switch>
<Route path='/path1' component={Path1Component}/>
<Route path='/path2' component={Path2Component}/>
<Route exact path='/' component={Home}/>
<Route component={NotFound}/>
</Switch>
This means that when i'm on a path: '/' i get a Home component, on '/path1' i get a Path1Component and on path '/foobar' i get a NotFound component. And that is perfectly fine
However when i'm on '/path1/foobar' route i also get the Path1Component. This behaviour is not correct in every case - this time i do not want any nested routes for '/path1' route. '/path1/foobar' should get a NotFound component, any string, with '/' or without after '/path1' should return NotFound component.
What would be the preferred resolution to this problem? I could just add exact to every path, but wouldn't that be overbloating the code? I feel like that should be the default, but it's not the case.
Even on React Router v4 docs, like here. I see this problem - here '/will-match/foo' will also match. What are your thoughts?
There is a discussion here, but to make it short : it would break existing code. If this were to change, you'd have to do exact={false} if you want to match child routes without doing 'path1/child1'.
I'm trying to migrate to use React Router 4 and having some trouble understanding the logic of the <Switch> component as it's used in the docs to handle a 404 (or unmatched) route.
For my entry JavaScript file, I have the following routes set up.
index.js
<Switch>
<Route path="/login" component={Login} />
<Route path="/forgot-password" component={ForgotPassword} />
<Route path="/email-verification" component={EmailVerification} />
<Route component={App} />
</Switch>
The Login component will check to see if the user is authenticated, and if so, redirect the user to the /dashboard route (via history.replace).
The App component is only accessible when the user is authenticated and it has a similar check to redirect the user to /login if she is not.
In my App component I have more specified routes that I can be sure are only accessible if the user is logged in.
App.js
<Switch>
<Route path="/dashboard" component={Dashboard} />
<Route path="/accounts" component={Account} />
<Authorize permissions={['view-admin']}>
<Route path="/admin" component={Admin} />
</Authorize>
<Route path="/users" component={Users} />
<Route component={NotFound} />
</Switch>
Herein lies my problem. The Authorize component checks against the permissions passed to see if the user has those permissions, if so, it renders the children directly, if not, it returns null from render().
The expected behavior here is that the <Route path="/admin" /> does not render at all when there are insufficient permissions and the <Route component={NotFound} /> component renders.
According to the docs:
A renders the first child that matches. A
with no path always matches.
However, if I go to any route declared after the <Authorize> component, the router is matching to null. This means that, based on the example above, going to /users returns null. Is the expected behavior of react-router to return the first match in a <Switch/> component, even if it's a null value?
How can I provide a "catch-all" route (404) for such a situation without creating a <PrivateRoute> component for each of the many, authenticated routes in App.js? Should a null value really produce a match?
Unfortunately, react-router's Switch component won't work with routes nested inside other components like in your example. If you check the docs for Switch, it says:
All children of a <Switch> should be <Route> or <Redirect> elements.
... so your Authorize component is not actually legal there as a direct child of Switch.
If you have a read through the source code of the Switch component, you'll see that it rather evilly reads the props of each of its children and manually applies react-router's matchPath method on each child's path (or from) prop to determine which one should be rendered.
So, what's happening in your case is Switch iterates through its children until it gets to your Authorize component. It then looks at that component's props, finding neither a path or from prop, and calls matchPath on an undefined path. As you note yourself, "a <Route> with no path always matches", so matchPath returns true, and Switch renders your Authorize component (ignoring any subsequent Routes or Redirects, since it believes it found a match). The nested '/admin' route inside your Authorize component doesn't match the current path however, so you get a null result back from the render.
I'm facing a similar situation at work. My plan to work around it is to replace react-router's Switch in my routing code with a custom component which iterates through its children, manually rendering each one in turn, and returning the result of the first one that returns something other than null. I'll update this answer when I've given it a shot.
Edit: Well, that didn't work. I couldn't work out a supported way to manually invoke "render" on the children. Sorry I couldn't give you a workaround to Switch's limitations.
In case anyone reads this in >= 2019, one way to deal with this behaviour is to simply wrap the Route-component like so:
import React from 'react'
import { Route } from 'react-router-dom'
type Props = {
permissions: string[]
componentWhenNotAuthorized?: React.ElementType
}
const AuthorizedRoute: React.FunctionComponent<Props> = ({
permissions,
componentWhenNotAuthorized: ComponentWhenNotAuthorized,
...rest
}) => {
const isAuthorized = someFancyAuthorizationLogic(permissions)
return isAuthorized
? <Route {...rest} />
: ComponentWhenNotAuthorized
? <ComponentWhenNotAuthorized {...rest} />
: null
}
export default AuthorizedRoute
Then, simply use it as such:
import React from 'react'
import { Route, Switch } from 'react-router-dom'
import AuthorizedRoute from 'some/path/AuthorizedRoute'
import Account from 'some/path/Account'
import Admin from 'some/path/Admin'
import Dashboard from 'some/path/Dashboard'
import NotFound from 'some/path/NotFound'
import Users from 'some/path/Users'
const AppRouter: React.FunctionComponent = () => (
<Switch>
<Route
component={Account}
path='/accounts'
/>
<AuthorizedRoute
component={Admin}
componentWhenNotAuthorized={NotFound}
path='/admin'
permissions={['view-admin']}
/>
<Route
component={Dashboard}
path='/dashboard'
/>
<Route
component={Users}
path='/users'
/>
<Route
component={NotFound}
/>
</Switch>
)
export default AppRouter
Similar idea to what Robert said, here's how I did it
class NullComponent extends React.Component {
shouldComponentBeRenderedByRoute() {
return false;
}
render() {
return null;
}
}
class CustomSwitch extends React.Component {
render() {
return (
// React.Children.map returns components even for null, which
const children = React.Children.toArray(this.props.children).map(child => {
const { render, shouldComponentBeRenderedByRoute } = child.type.prototype;
if (shouldComponentBeRenderedByRoute && !shouldComponentBeRenderedByRoute.call(child)) {
return null;
}
if (shouldComponentBeRenderedByRoute) {
return render.call(child);
}
return child;
});
return <Switch>{children}</Switch>;
);
}
}
then use it just do
<CustomSwitch>
<Route path... />
<NullComponent />
<Route path... />
</CustomSwitch>
here, a component without shouldComponentBeRenderedByRoute function is assumed to be a valid Route component from react-router, but you can add more condition (maybe use path props) to check if it's a valid Route
I am creating one app using reactJS. I am defining routing by react-router.
sample code is below:
<Router history={browserHistory()}>
<Route path="/" component={App}>
<Route path="taco/:name" component={Taco} />
</Route>
</Router>
but I want to use something like this:
<Route path="taco?yourchoice/:name" component={Taco} />
but I am not able to do that.
Any help would be appreciated.
Thanks
path="taco?yourchoice/:name is not a valid path. Because you include the forward slash between yourchoice and :name, I am not positive if you are trying to place a path segment after the query or capture the yourchoice query parameter value.
In the case of the former, you cannot place a path segment after the search segment of a URL and you will need to reorder the path. For the latter, React Router does not take into account the search string when it is matching the current location to route paths. However, the location will be parsed and the query parameters will be available on the location object (which is injected as a prop into the <Route>'s component.
So, given the route:
<Route path="taco" component={Taco} />
When the user navigates to the URL:
example.com/taco?yourchoice=fish
The Taco component will be rendered and one of its props will be location.
class Taco extends React.Component {
render() {
const { query } = this.props.location
return <div>A {query.yourchoice} taco</div>
}
}