React Router v4: <Route> component unable to match urls with question marks - reactjs

I'm using a React.js app as a widget inside a Wordpress admin area. The way some users configure wordpress urls can use query parameters to set the page view, for example:
domain.com/wpFolder/wp-admin/options-general.php?page=settingspage
I want to be able to set my Links and Routes to conform to this structure so that page reloads don't break. I am unable to do this, adding slashes/subfolders is fine but the moment I add ? the <Route /> no longer matches. Example:
<StyledMaterialLink // This is a <Link /> wrapped by Styled Components
component={RouterLink}
to={`/wptest2/wp-admin/options-general.php?`}>
<Tab
label="See Current Coupons"
value={seeCurrentCoupons}
onClick={() => {
console.log(location.href, `=====location.href=====`);
setAdminView(seeCurrentCoupons)
}}
/>
</StyledMaterialLink>
<Route
path={`/wptest2/wp-admin/options-general.php?`}
render={props => {
return (
<CurrentCouponChannel.Provider
value={state.currentCouponsAndTargets}>
<ViewCoupons />
</CurrentCouponChannel.Provider>
)
}}
/>
Eventually I would like the <Route path /> above to end with &section=view-coupons as well.
Is it possible to hardcode query string params this way to work after reloading with my framework?

Search parameters are not part of the path. You can access those values through props.location
<Route
path={`/wptest2/wp-admin/options-general.php`}
render={props => {
// do something with props.location.search
The library https://www.npmjs.com/package/query-string is helpful here.

Related

history.push() does not updates browser's url

<div key={id} onClick={() => clicked(id)}>{name}</div>
In this clicked function is called.
const clicked = (id) => {
history.push(`/user_dashboard/projects/${id}/designs`);
}
this is matched with the routes
<PrivateRoute path={match.url + '/:id/designs'} exact component={Designs} />
I have a route file with this route which opens dashboard with all the user projects
<PrivateRoute path='/user_dashboard/projects' exact component={Dashboard} />
In Dashboard it shows a toolbar with all the projects and if a user clicks any of the project it shows related designs corresponding to that project.
<Toolbar />
<PrivateRoute path={match.url} exact component={Projects} />
<PrivateRoute path={match.url + '/:id/designs'} exact component={Designs} />
I need to keep toolbar fixed in both the routes.What can I do?
Let me know if you find any mistake.
You can use Link from react-router-dom instead of programmatically updating the URL.
<Link to={`/user_dashboard/projects/${id}/designs`} key={id}>{name}</Link>
this will handle the url for you, under the hood it's an anchor tag.
You can learn more about it here
First error I can see is you are not using string interpolation for path property, so it should be
path={`${match.url}/:id/designs`}
I think you need to provide more details for us to properly help you out on this issue.

React Router Dom Pathname keeps nesting

I have a React app in which a search options is included in the navbar, and can be accessed anywhere in the app. Whenever a user searches and then clicks on one of the products show, it should send them to a detail view of that product.
The problem I am facing is with routing. Whenever I search and click in the product for the first time, it correctly redirects to, as an example, http://localhost:3000/catalogue/women/sportive-shoes-sneakers/sneakers--low-top-sneaker/292487. However, if once I am in that route I try to go send the user to another product, it formulates the url like this: http://localhost:3000/catalogue/women/sportive-shoes-sneakers/sneakers--low-top-sneaker/catalogue/men/denim-pants/jeans/293140. As you can see, it's nesting the pathnames one after the other.
My code looks like this:
this.state.displayProducts.map((product, index) => {
return (
<NavLink strict to={{
pathname: `catalogue/${StringUtil.getUrlFromString(product.line)}/${StringUtil.getUrlFromString(product.familyName)}/${StringUtil.getUrlFromString(product.subfamilyName)}/${product.id[0] || product.id}`,
selectedProduct: product
}} key={index}>
<div className="catalogue-productlist-product" onClick={this.props.wipeInput && this.props.wipeInput}>
<img src={product.images && product.images[0] ? product.images[0].replace('{authorization}', this.props.token) : this.props.placeholder}
alt="Category" onError={this.imgError} />
<div>
{product.productName}
</div>
</div>
</NavLink>
)
})
And my Route like this:
<Route
exact path="/catalogue"
render={(props) => (
window.location.search.length > 0 ?
<ProductList />
:
<Slider
{...props}
categories={this.props.categories}
/>
)}
/>
<Route
exact path={`/catalogue/search/:qs`}
component={ProductList}
/>
<Route
exact path={`/catalogue/:line/:family?/`}
render={(props) => (
<Slider
{...props}
categories={this.props.categories}
/>
)} />
<Route
exact path={`/catalogue/:line/:family/:subfamily`}
component={ProductList} />
<Route
exact path={`/catalogue/:line/:family/:subfamily/:product`}
component={ProductDetail} />
How would one go about it working as intended? ie: no matter the current route, the user should always be sent to wherever the navlink send them.
The problem lies here in your pathname, you should always consider add a leading slash in your pathnames just like this:
...
pathname: `/catalogue/${StringUtil.getUrlFromString(product.line)}/${StringUtil.getUrlFromString(product.familyName)}/${StringUtil.getUrlFromString(product.subfamilyName)}/${product.id[0] || product.id}`
...
If you don't use this leading slash in your link navigation it will always append the path you provided to the previous path that you provided, so it will make your first redirect nice and tidy then the upcoming ones will append to the existing path that you are in right now.
NOTE: If you always want to change the whole directory with each product selected consider adding leading slash in your pathname, otherwise, you should use an alternative way.
I think i know what the problem is, I am not 100% sure but going to go out on a limb, because I have encountered similar issues in the past, I think you need to add a '/' to the pathname value before 'catalogue'.
...
return (
<NavLink strict to={{
pathname: `/catalogue/${StringUtil.getUrlFromString(product.line)}/${StringUtil.getUrlFromString(product.familyName)}/${StringUtil.getUrlFromString(product.subfamilyName)}/${product.id[0] || product.id}`,
...
Because it is not prefixed with '/', it is appending the path to the current path rather than starting it from the beginning.

react-router routes <Link> v5

I am trying to understand nested react routes. My current issue is that my link outputs undefined and also renders the component on the same view instead of loading just that component view.
Events.js
const data = {
"events":[
{
"id":"1234",
"name":"Meetup",
"description":"Tech meetup"
},
{
"id":"34234",
"name":"Test",
"description":"Events"
}
]
}
{data.events.map(event => (
<Link to={`/events/${data.events.id}`}>
<EventCard data={event} />
</Link>
))}
<Switch>
<Route path={`/events/:eventId`} render={(props) => <Event {...props} />}/>
</Switch>
How can I render the Event component when a user has clicked one of the event card. I can currently load both components on the same page but that is not my desired output. The expected path should be:
Expected:
Render only
Current:
Renders & on same page
At first you have got a problem here,
<Link to={`/events/${data.events.id}`}>
Since you are already mapping event object from data array, iterator event already holds event data. So you should change it like this;
<Link to={`/events/${event.id}`}>
Also I'd strongly recommend to use 'exact' prop in your routes for routing multiple endpoints from same root like this.
<Route exact path={`/events/:eventId`} render={(props) => <Event {...props} />}/>
See here for further information. Difference between exact path and path
You need to add id param to your event endpoint:
<Route path="/events/:id?" component={Event} />
Then in the Event you can check if id is present via props.match.params.id.

React Router render components with /:username or /component/

I am trying to implement a routing structure where a user goes to another user's page or their own when the path is /:username. I also want to render another page with a path /watch or /watch/ .. Facebook has a similar setup where /:username will take you to your page or another user's and /watch/ for example is a page. Is there best practice to achieve this with react-router?
As of now I have something like this..
<Route path="/" exact component={authenticated ? Home : Index} />
<Route path="/watch/" component={Watch} />
<Route path="/:username" exact component={({match}) => {
if(match.params.username === data.Username) {
return <ProfilePage match={match} />
} else {
return <UserPage match={match} />
}
}} />
Now if I got to /watch/ the profile component is being rendered aswell. So :username is going to match all my routes?
As you already deducted, /:username is matching at the same time as /watch/ because both patterns match the URL /watch/.
Thankfully, React Router provides a <Switch> component for cases like this one, where only the first match is rendered:
<Switch>
<Route path="/watch/" component={Watch} />
<Route path="/:username" component={...} />
</Switch>
Now, with the URL /watch/, only the first route is rendered, even though the second one matches too.
If you are using react-router-dom v6, do these:
instead of Switch, you should use Routes
instead of component={<SomeComponent />} property, use element={<SomeComponent />}
Just in case, you can Read this article about upgrading from v5 to v6

Advantages of dynamic vs static routing in React

I'm reading about static vs dynamic routing in React Router, and I'm struggling to identify the advantages of the latter (and why v4 chose to go with it). I can see the advantage of listing out all the routes for an application (static), as well as the component that each route maps to, allowing you to trace what would be rendered given a specific URL. But I'm not seeing any clear advantage to dynamic routes.
If anything, I can only see disadvantages, because there is no clear way to see what state a URL will map to, without starting at the root app element and working your way through the routes (though I might be mistaken).
What situations does dynamic routing address? Why is it preferable to static routing (maybe specifically in React apps)?
Dynamic Routing
From the react router docs:
When we say dynamic routing, we mean routing that takes place as your
app is rendering, not in a configuration or convention outside of a
running app.
Think of routes as components
The earlier versions of react-router (pre v4) used to have static routes. This led
to a centralized routing in apps like:
<Router>
<Route path='/' component={Main}>
<IndexRoute component={Home} />
<Route path='about' component={About} />
<Route onEnter={verifyUser} path='profile' component={Profile} />
...
</Route>
</Router>
However, this is not exactly the React way of doing things. React focuses on composition using components based logic. So, instead of imagining our Routes as a static system, we can imagine them as components, which is what react-router v4 brings in and the primary philosophy behind it.
Therefore, we can use Route as we would use any React component. This lets us add Route components as and when we build different components. One advantage of doing this is we can decouple the routing logic to the components needing them.
Nesting routes
The About component can handle all the routes and conditionally render parts of UI based on the url (say /about/job or /about/life etc).
Another thing to note is that a Route component will either render the component for a matching route or null. Example, the following Route renders the About component for a route /about and null (or nothing) otherwise.
<Route path='about' component={About} />
This is also similar to how we're used to conditionally rendering components in React:
route === '/about' ? <About /> : null
Now if we need to render some other components inside the About component for routes /about/job or /about/life we can do it like:
const About = ({ match ) => (
<div>
...
<Route path={`${match.url}/job`} component={Job} />
<Route path={`${match.url}/life`} component={Life} />
</div>
)
Dynamic imports and code splitting
Personally, I've also found this approach works better for me in case I'm using dynamic imports with code-splitting, since I can add dynamic routes in any of my components. For example,
import Loadable from 'react-loadable';
const Loading = () => (
<div />
);
const Job = Loadable({
loader: () => import('./Job'),
loading: Loading,
});
const Life = Loadable({
loader: () => import('./Life'),
loading: Loading,
});
...
render() {
return (
...
<Route path={`${match.url}/job`} component={Job} />
<Route path={`${match.url}/life`} component={Life} />
)
}
Responsive routes
Another great use case for dynamic routing is creating responsive routes which is explained beautifully in the react router docs and a recommended read. Here's the example from the docs:
const App = () => (
<AppLayout>
<Route path="/invoices" component={Invoices}/>
</AppLayout>
)
const Invoices = () => (
<Layout>
{/* always show the nav */}
<InvoicesNav/>
<Media query={PRETTY_SMALL}>
{screenIsSmall => screenIsSmall
// small screen has no redirect
? <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
</Switch>
// large screen does!
: <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
<Redirect from="/invoices" to="/invoices/dashboard"/>
</Switch>
}
</Media>
</Layout>
)
Summarizing the docs, you'll notice how simple and declarative it becomes to add the Redirect to large screen sizes using dynamic routing. Using static routing in such cases would be quite cumbersome and would need us to put all the routes in a single place. Having dynamic routing simplifies this problem since now the logic becomes composable (like components).
Static Routing
There are some problems which are not solved easily with dynamic routing. An advantage of static routing is that it allows for inspection and matching of routes before rendering. Hence it proves useful especially on server side. The react router team is also working on a solution called react-router-config, quoting from which:
With the introduction of React Router v4, there is no longer a
centralized route configuration. There are some use-cases where it is
valuable to know about all the app's potential routes such as:
Loading data on the server or in the lifecycle before rendering the next screen
Linking to routes by name
Static analysis
Hope this provides a good summary of both Dynamic Routing and Static Routing and the use cases for them :)
According to the React-Router docs:
When we say dynamic routing, we mean routing that takes place as your
app is rendering, not in a configuration or convention outside of a
running app. That means almost everything is a component in React
Router.
Its clear for the explanation that, all you Routes are not initialised at the start of your application,
In React-router v3 or below, it used static Routes and all Routes would be initialised at the top level, and nesting used to be achieved like
<Router>
<Route path='/' component={App}>
<IndexRoute component={Dashboard} />
<Route path='users' component={Users}>
<IndexRoute component={Home}/>
<Route path="users/:id" component={User}/>
</Route>
</Route>
</Router>
With this API setup, react-router was reimplementing parts of React (lifecycles, and more), and it just didn’t match the composition logic that React recommends on using.
With Dynamic Routes the following advatages, comes to be foreseen
Nested Routes
Nested Routes with Dynamic Routing are more like
const App = () => (
<BrowserRouter>
{/* here's a div */}
<div>
{/* here's a Route */}
<Route path="/todos" component={Todos}/>
</div>
</BrowserRouter>
)
// when the url matches `/todos` this component renders
const Todos = ({ match }) => (
// here's a nested div
<div>
{/* here's a nested Route,
match.url helps us make a relative path */}
<Route
path={`${match.path}/:id`}
component={Todo}
/>
</div>
)
In the above example, only when /todos matches the route-path, the Todo component is mounted and only then the Route path /todos/:id is defined.
Responsive routes
The React-router docs have a good use case for this.
Consider a user navigates to /invoices. Your app is adaptive to different screen sizes, they have a narrow viewport, and so you only show them the list of invoices and a link to the invoice dashboard. They can navigate deeper from there.
However on a large screen, navigation is on the left and the dashboard or specific invoices show up on the right.
and hence /invoices is not a valid Route for a large screen and we would want to redirect to /invoices/dashboard. This may so happen, the user rotates his/her phone from a portait to a landscape mode. This can easily be done using dynamic Routing
const Invoices = () => (
<Layout>
{/* always show the nav */}
<InvoicesNav/>
<Media query={PRETTY_SMALL}>
{screenIsSmall => screenIsSmall
// small screen has no redirect
? <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
</Switch>
// large screen does!
: <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
<Redirect from="/invoices" to="/invoices/dashboard"/>
</Switch>
}
</Media>
</Layout>
)
Using Dynamic Routes with React Router’s, think about components, not static routes.
Code Splitting
One great feature of the web is that we don’t have to make our visitors download the entire app before they can use it. You can think of code splitting as incrementally downloading the app. This is made possible with Dynamic Routing.
The advantages it brings is that all your code need not be downloaded at once and hence it makes initial rendering faster.
Here is a good article that helps you setUp codeSplitting for your application
Writing Composable Authenticated Routes
With Dynamic Routing its also made easier to write PrivateRoutes(an HOC that does authentication) which allow for authenticating users and providing them access to specific Routes and redirecting otherwise. This call all me made very generically
A Typical Private Route would be look like
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
fakeAuth.isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
and can be used as
<PrivateRoute path="/protected" component={Protected} />

Resources