Pass URL params in route - ReactJS - javascript

I have a URL
https://website.com/enable/code=react/string=true
My React router looks like this
<Router history={history}>
<Route
path="/"
component={App}
>
<IndexRoute component={Page1} />
<Route path="/enable/language='dynamicVal'/string='dynamicVal'>{page2()}</Route>
</Route>
</Router>
Now the Route path i need to get the dynamic value for language and string
I have tried this.
/enable/language=(/:react)/string=(/:true)
How and where to define the dynamic values?

You can go with the path param approach and structure your Routes like
<Router history={history}>
<Route
path="/"
component={App}
>
<IndexRoute component={Page1} />
<Route path="/enable/:language/:codeString'>{page2()}</Route>
</Route>
</Router>
and in your component you get get the path params like
this.props.params.language and this.props.params.codeString if you are using react-router-v3 or below
and
this.props.match.params.language and this.props.match.params.codeString if you are using react-router-v4

Assuming react-router v4 (not tested on v3, which is what you seem to be using?), you can define a Route as such:
<Route
exact path="/enable/language=:language/string=:stringVal"
component={YourComponent} />`
In YourComponent, both params will be available under this.props.match.params.language and this.props.match.params.stringVal, respectively.
See this CodeSandbox for a working example.

Related

React Router - How to IndexRedirect to dynamic route

I have a situation in my React app to look like something as follows using react-router. I want the index of the users/:userId endpoint to redirect to another endpoint that includes a :userId params. What is a good way to do this? Right now the user from currentUser will return null because the code is only executed once in the beginning when the App is loaded the user is not yet authenticated. I'm guessing I will need to force react-router to reload the history and certain variables, or change the order of when I do authentication?
routes.js
{ /* Routes requiring login */ }
<Route onEnter={requireLogin}>
<Route path="users" component={Users}>
<Route path=":userId" component={User}>
<IndexRedirect to={`/users/${currentUser().id}/profile`} />
<Route path="profile" component={UserProfile} />
<Route path="settings" component={UserSettings} />
<Route path="activity" component={UserActivity} />
</Route>
</Route>
</Route>
If I understand it correctly and you want to redirect from /users/1 to /users/1/profile and /users/2 to /users/2/profile, you can simply replace ${currentUser().id} with :userId.
So your code would looks like this:
<Route onEnter={requireLogin}>
<Route path="users" component={Users}>
<Route path=":userId" component={User}>
<IndexRedirect to="profile" />
<Route path="profile" component={UserProfile} />
<Route path="settings" component={UserSettings} />
<Route path="activity" component={UserActivity} />
</Route>
</Route>
</Route>
Because the redirect is in the context of the route :userId: you can simply define your target route relative.

React Router doesn't match all optional parameter

I'm facing a situation where I have my router working fine with the following configuration:
<Route path="/" component={Blog}>
<Route path="list(/:category)(/:subcat)" component={ArticleList} />
<Route name="article/:category(/:subcat)/:id" component={ArticlePage} />
</Route>
But now I was asked to improve the URL and remove some "unecessary" prefixes from the URLs
Current URL -> Desired URL
blog/list/football/worldcup -> blog/football/worldcup
blog/list/football -> blog/football
blog/article/football/10 -> blog/football/10
So I tried unsuccessfully to change my route to keep all parameters optional
<Route path="/" component={Blog}>
<Route path="(/:category)(/:subcat)" component={ArticleList} />
<Route name=":category(/:subcat)/:id" component={ArticlePage} />
</Route>
Is there a way to match the urls as described using react router?
You can simply have it like this
<Route path="blog" component={Blog}>
<IndexRoute component={ BlogComponent } />
<Route path=":category" component={ ArticleCategoryList } />
<Route path=":category/:subcat" component={ ArticleSubCategoryList } />
</Route>
I have prefixed blog to the route.
IndexRoute will match /blog route, it acts as the index route.
:category will match blog/football or blog/any-sport
:category/:subcat will match both blog/football/worldcup and blog/football/10
You will have to be careful here as this will match for both subcategory and id.
You can write a function/middleware which can validate the type of route.. whether id or subcategory.

React Router and Authentication

I'm new to react and learning how to react-router to achieve routing. As a part of my sample app I was trying to put an authenticator on every path that's a part of the site. I'm following this example: https://github.com/reactjs/react-router/blob/master/examples/auth-flow/app.js
And here is my sample code:
render(
<Router history={browserHistory}>
<Route path="/" component={App} onEnter={requireAuth}>
<IndexRoute component={Home}/>
<Route path="login" component={Login}/>
<Route path="logout" component={Logout}/>
<Route path="about" component={About}/>
<Route path="inbox" component={Inbox}/>
</Route>
</Router>,
document.getElementById("app"));
And here is what requireAuth does:
function requireAuth(nextState, replace) {
if(!auth.isUserLoggedIn()) {
replace({
path: "/login",
state: {nextPathname: nextState.location.pathname}
});
}
}
The difference between what I'm doing and what the example does is that the example puts the auth functionality only on one route (dashboard), where as I want to put it on every route. But the example stops working and starts throwing error
Maximum call stack size exceeded
and I think rightly so because /login is a subroute of / and because requireAuth is redirecting to the sub route, it just try to authenticate again and keeps trying for ever. I'm probably missing a simple fix here to get this to work, but am not able to figure what is the best way to do it.
What can I do to enable authentication at every level of the site?
Answer of #DemoUser is almost correct and #KumarM how you can login if you need to be authenticated to see login page ? here is what you can do
render(
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="login" component={Login}/>
<Route path="logout" component={Logout}/>
<Route path="/dashboard" onEnter={requireAuth} >
<IndexRoute component={Home}/>
<Route path="about" component={About}/>
<Route path="inbox" component={Inbox} />
</Route>
</Route>
</Router>,
document.getElementById("app"));
whats happening here ?
login and logout page need not to be authenticated
all child components of Dashboard will automatically be authenticated
I think you must've figured it out by now but I stumbled across this today, so I thought of sharing this:
If you are adamant on securing every resource of your application, I'd suggest thinking twice. What #abhirathore2006 said is: users of your application shouldn't be authenticated to access the login component. Should they?
You are correct in assuming that the 'Maximum call stack size increased' is because of infinite redirects happening and you can try to avoid it by doing a null check or something.
The point is, you'd just need to place the onEnter hook on the /logout, /inbox & /about components and leave the /login as is. Otherwise you could try wrapping these components in a HOC or use the newer react-router4 approach to get what you need.
Hope this helps!
Cheers!
You could change your route so that, the pages that do not require login, can be rendered without need of authentication, as:
render(
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={Home}/>
<Route path="login" component={Login}/>
<Route path="logout" component={Logout}/>
<Route path="about" component={About}/>
<Route path="inbox" component={Inbox} onEnter={requireAuth}/>
</Route>
</Router>,
document.getElementById("app"));
If user is already loggedin requireAuth does not do anything, and your Inbox component is rendered.

IndexRoute sub-routes

I have a web site with few ordinary pages and a page with Google Map. When map marker is clicked a panel with marker details is displayed next to the map. This detail has own URL so that users can link to it:
<Route path="/" component={App}>
<IndexRoute component={Welcome} />
<Route path="map" component={Map}>
{/* Detail is a child component of Map,
it only adds detail panel markup to Map. */}
<Route path="detail/:id" component={Detail} />
</Route>
<Route path="about" component={About} />
</Route>
This works fine. But let's get rid of Welcome page and display Map right on the web root so that:
/ renders App > Map components
/detail/:id renders App > Map > Detail components
/about renders App > About components
<Route path="/" component={App}>
{/* Map has to be IndexRoute so that it is displayed at root URL. */}
<IndexRoute component={Map}>
<Route path="detail/:id" component={Detail} />
</IndexRoute>
<Route path="about" component={About} />
</Route>
But this doesn't work because IndexRoute can't have subroutes.
This is the best solution I have found:
<Route path="/" component={App}>
<Route component={Map}>
<IndexRoute component={EmptyComponent} />
<Route path="detail/:id" compoent={Detail} />
</Route>
<Route path="about" component={About} />
</Route>
But I don't like the empty component.
Am I missing something? Am I doing something unusual? Why it is not possible to do it the first more intuitive way?
Move the / path
<Route component={App}>
<Route path="/" component={Map}>
<Route path="detail/:id" component={Detail}/>
</Route>
<Route path="/about"/>
</Route>
Your solution looks largely fine to me – the only caveat is that you don't need to specify the component in that case; just do <IndexRoute />.
By design, index routes terminate matching, but it's easy to insert trivial routes.
Maybe I am wrong but it seems as if your tried to set another route in:
<IndexRoute component={Map}>
<Route path="detail/:id" component={Detail} />
</IndexRoute>
So your basic structure is something like:
<IndexRoute>
<Route> </Route>
</IndexRoute>
According to your error it is not allowed that there is a <Route> inside of your <IndexRoute>... At the beginning you did not do that mistake because you closed the <IndexRoute> before you opened the next <Route>-Tag.
So if you want your code to work again you should not open another <Route> inside of your <IndexRoute>. You managed to fix this by adding an Dummy-IndexRoute. So if you want to set your Map component as IndexRoute you will have to change your HTML structure so that there is no Detail component inside of your map component because then you will have the same problem again that you got a <Route> inside your <IndexRoute>

How to mimic DefaultRoute in React Router 1.0

So, switching to the latest React Router (1.0.0RC3). I have run into a piece of old functionality that I can't find how to replicate with the new 1.0 API.
In my Router, I always render a top-level App component, then a second-level Layout component, then a Page component. In the old React Router, I didn't have to put a path property on a Route, so I could "group" certain routes to have a parent component without adding another level to my url.
Below, you'll see that when hitting the / route, I try to load App, DefaultLayout, and Home. However, it won't render DefaultLayout without an explicit path property. So if I put path="app" on my default layout, it works, but I'm trying to not change my homepage route if possible.
I've tried leaving path off, putting an absolute path, using nested IndexRoutes (doesn't work). Is there still a way to do this in RR 1.0?
export const routes = (
<Router history={createBrowserHistory()}>
<Route path="/" component={App}>
<Route component={DefaultLayout}> // Requires `path` Here
<IndexRoute component={Home} />
<Route path="about" component={About} />
<Route path="contact" component={Contact} />
<Route path="careers" component={Careers} />
</Route>
<Route path="blog" component={BlogLayout}>
<IndexRoute component={BlogHome} />
<Route path="posts/:post_name" component={BlogPost} />
</Route>
</Route>
</Router>
);
If I understood you correctly, your routes should look like:
export const routes = (
<Router history={createBrowserHistory()}>
<Route component={App}>
<Route path="/" component={DefaultLayout}> // Requires `path` Here
<IndexRoute component={Home} />
<Route path="about" component={About} />
<Route path="contact" component={Contact} />
<Route path="careers" component={Careers} />
</Route>
<Route path="blog" component={BlogLayout}>
<IndexRoute component={BlogHome} />
<Route path="posts/:post_name" component={BlogPost} />
</Route>
</Route>
</Router>
);

Categories