So I'm using the npm package react-router-relative (https://www.npmjs.com/package/react-router-relative) but it doesn't seem to be switching the url properly.
Here's what my links looks like:
<Link to='items' className="btn btn-default submission-button">Items</Link>
<Link to='maps' className="btn btn-default submission-button">Maps</Link>
Here's my routing:
<Route path="submissions" component={SubmissionPage}>
<Route path="items" component={ItemSubmissions}></Route>
<Route path="maps" component={MapSubmissions}></Route>
</Route>
what happens is the first time I click on the link it'll link properly i.e.
http://localhost:3000/#/account/submissions/items
but when I hit it again it'll go to:
http://localhost:3000/#/account/submissions/items/items
at this point the end part will switch rather than append, but throws an error.
However, I'm trying to make the part right after 'submission' switch, i.e. account/submissions/items account/submissions/maps.
What am I doing wrong?
I've tried a non-relative link variation of this, i.e. {this.props.location.pathname + '/items'} but it just does the same thing.
First, let's emphasis on the concept of relative link.
It is a link that will bring you somewhere depending on where you already are.
Meaning, result will vary if you use the same relative link in different places (URLs). The direct implication is that if you want only one given behaviour for a relative link, you cannot use it at several places.
What happens in your case is that the same relative link appears for different URLs, leading to different results.
What can you do:
As pointed earlier, you could use currentPath props, suggested in the page you linked here.
The result would be something like:
<Link to='items' currentPath='/submissions'>Items</Link>
<Link to='maps' currentPath='/submissions'>Maps</Link>
Which looks like a disguised absolute path:
<Link to='/submissions/items'>Items</Link>
<Link to='/submissions/maps'>Maps</Link>
A solution for relative paths would be:
<Link to='../items'>Items</Link>
<Link to='../maps'>Maps</Link>
But keep in mind that those links, being relative, should only be displayed in one place, in your case #/submissions/somewhere
If you want a link that brings you to the same page regardless of the current location, you should use absolute links.
So far, I haven't come up with many uses for relative links.
Go back: <Link to="..">Go back</Link>
Or maybe common actions: <Link to="./edit">Edit</Link>
To conclude, I would say that there is no need for relative links when only one behaviour is expected. One behaviour means one route, and if you know the route, you might as well use an absolute link. Relative links should be used only when you expect different behaviours.
I have searched for your problem and i found this useful package react-router-relative-links
To install it run in your terminal :
npm install react-router-relative-links react-router-apply-middleware
then in you routes file you will apply the middleware like so:
import { Router, Route, browserHistory } from 'react-router';
import applyMiddlewareRouter from 'react-router-apply-middleware'
import { useRelativeLinks } from 'react-router-relative-links'
<Router history={browserHistory} render={applyMiddlewareRouter(useRelativeLinks())}>
<Route path="submissions" component={SubmissionPage}>
<Route path="items" component={ItemSubmissions}></Route>
<Route path="maps" component={MapSubmissions}></Route>
</Route>
</Router>
then any time you you need to add a navigate Link you can add it like so:
we will assume the current path is localhost:8080/account/submissions
<RelativeLink to='./items'>test</RelativeLink> # to localhost:8080/account/submissions/items
<RelativeLink to='./maps'>test</RelativeLink> # to localhost:8080/account/submissions/maps
, However it's better to use the Link component provided by react-router
if you pass currentPath props to your link it will start work. otherwise, if you don't define the current path. it falls to window.location.hash which is not good.
<Link to='/items' currentPath='/submissions' className="btn btn-default submission-button">Items</Link>
<Link to='/maps' currentPath='/submissions' className="btn btn-default submission-button">Maps</Link>
I believe the npm package react-router-relative is a bit buggy.
personally, I don't recommend you to use in production.
I was not able to find any other approach but this. Unfortunately react-router doesn't support the relative paths but the work around would be using the Switch and then generating absolute paths in your components if it is suitable for your use. You have to update your react-router to 4+ before importing the Switch method.
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
<Router>
<Switch>
<Route path="submissions" component={SubmissionPage} />
<Route path="items" component={ItemSubmissions} />
<Route path="maps" component={MapSubmissions} />
</Switch>
</Router>
But probably if I were to use only two path as you are doing which ends up being /accounts/submissions/items and /maps I would probably just stick with the <Route path="/accounts/submissions/items" component={MapSubmissions} />
P.S : I am just pointing out my approach and solution. I am not sure whether it would cause other problems or not.
Related
I am building a website with the following link structure:
/blog
/blog/post1
/preview
The /blog url lists all the blog posts, the /blog/post1 shows the post in edit view and the preview button in the same edit view links to /preview url that shows the preview of the edited version.
My initial idea was to use a /blog/post1/preview url but with the use of <Switch> it became complicated. It did not work because of the whole exact issue where I had to correctly match three urls that start with the same address. So I switched to /preview. Now, I can get the first to urls working but the /preview renders nothing on the screen. Is it because of where my <Link> and <Route> are defined in my app. I have following structure:
<Switch>
<Route path='/blog'>
<Blog/>
</Route>
<Switch>
in the main app.js
<Route exact path='/blog'>
//div with some helper views like search bar
</Route>
//it is inside map function that renders a card view of many posts, post1 for simplicity
<Route path='/blog/post1'>
<PostEditor/>
</Route>
in the main Post.js component
<Route path='/preview'>
//Content
</Route>
in the main PostEditor.js
The PostEditor.js has the Link and Route tags in the same file but not others. Is it a reason that it renders nothing. When I move the Route tag from PostEditor, to App.js, everything works. Is it a nesting issue or something like that. I tried searching online but did not have much luck with multiple levels of nesting, usually it is two. Any help is greatly appreciated.
I am using react-router 5.2.0 and HashRouter as it works better with github
Update:
I have added a sample sandbox to mimic my scenario. Why is the /preview link not rendering anything in this case? I would like to know what I am doing wrong in this case, cause I feel there is a knowledge gap about something about react-router that I am missing about creating Links and Routes at different level? Additionally, what would be the best alternative to handle what I am currently doing.
Code SandBox
I don't see any route nesting here, but I suspect the issue you are running into is with path specificity and order.
Within the Switch you should order your routes to list more specific paths before less specific paths. Recall that the Switch returns and renders the first match is finds, so by trying to match more specific paths first you avoid accidentally matching a less specific path prefix. In other words, "/blog" is a path prefix for the other sub-routes so you want to match it last.
"/blog/:postId/preview"
"/blog/:postId"
"/blog"
Code:
<Switch>
<Route path='/blog/:postId/preview'>
//Content
</Route>
<Route path='/blog/:postId'>
<PostEditor/>
</Route>
<Route path='/blog'>
<Blog/>
</Route>
<Switch>
Edit
Why is the /preview link not rendering anything in this case? I would
like to know what I am doing wrong in this case, cause I feel there is
a knowledge gap about something about react-router that I am missing
about creating Links and Routes at different level?
The "/preview" link renders nothing because the component rendered on "/blog/post1" (i.e. Post) is now no longer being rendered. In other words, the URL is updated to "/preview" and Post is unmounted. Since you've no "/preview" route in the root nothing is matched and rendered.
Solution based on your added codesandbox
You can certainly nest routes. Here are the changes I suggest to properly nest blog routes.
App.js - Specificity and order matters, place more specific root paths before less specific paths.
<Switch>
<Route path="/blog">
<Blog />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
Blog.js - Render nested routes into another Switch component, again paying attention to path specificity and order. Use the useRouteMatch hook to access the current path and url props, path for nested route matching and url for nested links. Notice the route from Post for the preview is moved here.
const Blog = () => {
const { path, url } = useRouteMatch();
return (
<div className="blog">
<Switch>
<Route path={`${path}/:postId/preview`} exact>
Blog Preview Before Save
<br />
</Route>
<Route path={`${path}/post1`}>
<Post />
</Route>
<Route path={path}>
Blog Search Bar
<br />
<Link to={`${url}/post1`}>Post 1</Link>
</Route>
</Switch>
</div>
);
};
Post.js - Again, access the url from useRouteMatch to construct the nested link to the preview route.
const Post = () => {
const { url } = useRouteMatch();
return (
<div className="post">
<Link to={`${url}/preview`}>Preview</Link>
</div>
);
};
Demo
Note: Whether or not you "nest" Route components doesn't really matter in the grand scheme of things, the overall path structure will be identical. You can just as easily (probably easier in fact) define all your routes at the root level, and this is common and perfectly acceptable. The trade off may be in maintenance if you want to "nest" a sub-route elsewhere, you'll need to "find-and-replace" all the particular intermediate path segments versus the "nesting solution" you only need to update the path root/prefix. The "nesting solution" is more dynamic in this sense.
With route /preview we can not identify which blog we are viewing. Pasting a minimum working route configuration bellow
import React, {useState, useEffect} from 'react'
import {Link, BrowserRouter, Switch, Route, useParams} from 'react-router-dom'
#app.jsx
<Switch>
<Route path="/blog" exact>
<Blogs />
</Route>
<Route path="/blog/:id" exact>
<PostEditor />
</Route>
<Route path="/blog/:id/preview">
<Blog />
</Route>
</Switch>
const Blogs = () => {
return(
<h1>Blog list</h1>
)
}
const Blog = () => {
const {id} = useParams()
return(
<h1>Blog {id}</h1>
)
}
const PostEditor = () => {
const {id} = useParams()
return(
<h1>Edit Blog {id}</h1>
)
}
Update:
As I uderstand, You need to display preview right after editing form without a routing switch. For that a button wich trigger show preview element can be used.
import React, {useState} from "react";
import { Route } from "react-router-dom";
import { HashLink as Link } from "react-router-hash-link";
const Post = () => {
const [showPreview, setShowPreview] = useState(false);
const [editData, setEditData] = useState('Some Post attributes')
return(<div className="post">
{showPreview ? <Preview editData={editData} /> : null}
<button onClick={() => {setShowPreview(true)}}>Preview</button>
</div>)
};
Preview = ({editData}) => {
return(
<h1>{editData}</h1>
)
}
export default Post;
Basically, if I click on one of the navbar links, i.e. hotmelt, pressureSensitive, liquids.
http://localhost:3000/hotmelt ,
http://localhost:3000/pressure-sensitive ,
http://localhost:3000/liquid
Then click one of the types of adhesives listed under one of those pages, ie fg3000
http://localhost:3000/pressure-sensitive/fg3000
it shows the item clicked. BUT, when I click on another navbar link,
i.e. hotmelt,
instead of removing the pressure-sensitive/fg3000 and looking like this: http://localhost:3000/hotmelt
I get this: http://localhost:3000/pressure-sensitive/hotmelt
so I have one navlink path sitting in front of the other in the url, thus not showing anything on the page.
the only fix i have currently, is using the back button in the browser. and that lets the url follow the logical order.
I'm assuming I'm using a reusable component as a variable link in a way that maybe is causing the issue.
here's app.js
<Route exact path="/hotmelt" component={Hotmelt} />
<Route exact path="/hotmelt/:id" component={SingleAdhesive} />
<Route exact path="/liquid" component={Liquid} />
<Route exact path="/liquid/:id" component={SingleAdhesive} />
<Route exact path="/pressure-sensitive" component={PressureSensitive} />
<Route exact path="/pressure-sensitive/:id" component={SingleAdhesive} />
Notice how SingleAdhesives is used multiple times. Depending on one of three different adhesives. I have a feeling that's mixing things up? I'm just not sure how else to go about it?
Here's my git repo for this web app
Thank you for your thoughts/comments!
I was doubting in the answer so I downloaded the repo to test it. On your navbar component, your Link's components have something like this:
<Link to="hotmelt" className="nav-link text-white" >Hot Melt<span className="sr-only"></span></Link>
Just add a / before the hotmelt like this:
<Link to="/hotmelt" className="nav-link text-white" >Hot Melt<span className="sr-only"></span></Link>
...and it will work fine!
I decided I need to stop using HasRouter, and instead, use BrowserRouter.
I switched out all my imports from:
import { HashRouter} from 'react-router-dom';
to
import { BrowserRouter } from 'react-router-dom';
As well as all my blocks:
<HashRouter>
to
<BrowserRouter>
My app nearly behaved as expected. All links in my app work fine.
For example:
<Link to={{pathname: `/budget/${item.id}`, state: params}}>{item.name}</Link>
Updated my URL as expected and navigates to the component.
My history works too:
handleEditRow(paymentNumber) {
this.props.history.push(`/schedule/${this.state.scheduleId}/event/${paymentNumber}`);
}
However, all my NavBar buttons fail. The URL updates as expected, but my app does not load the component:
I have my routes in :
return (
<Switch>
<Route exact path="/" component={Dashboard} />
<Route exact path="/accounts" component={Accounts} />
....
</Switch>
)
}
I'm assuming this works, as the links above are behaving.
In my NavBar, I just do simple links:
<Nav.Link as={NavLink} to='/accounts' exact>Accounts</Nav.Link>
I found a link that mentioned to use 'Link' instead of NavLink.
<Nav.Link as={Link} to='/accounts' exact>Accounts</Nav.Link>
But this still fails. URL updates, but page doesn't change.
As I say, the URL changes as expected, but the app does not load the app.
However, if I then select the URL in the address bar, and press Enter (Load the URL that it changed to), the app loads the correct component, as expected.
Can anyone spot my error?
It was working fine with HashRouter.
Just a quick tip: if you're not sure about which Router to use, you can import it aliased as "Router". You then don't have to change all HashRouter references to BrowserRouter if you decide to switch to another Router:
import { BrowserRouter as Router } from 'react-router-dom';
And then use:
<Router>
The advantage of this approach is that you only change the import and it works straight out of the box.
I am currently in process of making a website, and hit a wall on this. I am using react-router-dom for routing my app, and kinda new to it, but in one page, the link that I put on the page does not change the URL
I already tried on previous component, in which I put the button on one component, then use the Link to load another component based on the Link and Route given. However, on this one, despite using similar structure as before, does not work at all
This is the code for the page that loads. I already imported the BrowserRouter as Link from react-router-dom
<div>
<p>TEST</p>
<Link to="/leadslist"><button class="btn btn-success">Back to Leads List</button></Link>
</div>
This is the target page
<div>
<Router>
<Switch>
<Redirect from="/leads" to="/leadslist" />
<Route path="/leadsForm" component={LeadsForm} />
<Route path="/leadslist" component={LeadsList} />
<Route path="/leaddetails" component={LeadsDetails}/>
</Switch>
</Router>
</div>
my previous try clicking button from "/leadslist" to "/leadsForm" actually worked, but then I try to load to "leaddetails" to try to go back to "/leadslist", but it doesn't change the URL at all. Any help will be truly appreciated.
You should not be importing BrowserRoute as Link from the react-router-dom. Both are different things. You need to import Link directly from react-router-dom. Rest, it should work solid.
You should be importing link this way:
import { Link } from "react-router-dom";
Now, I am not sure what's exactly happening your app. But here, I have created a very basic demo or working react-router using CodeSandBox. You can take a look, it should help you fix the problem you are having.
Also, you can read this article to further understand the working of the react router.
Your code doesn't work because you have added button inside Link tag so on click of button React only fires onClick method of button and Link will no be clicked!
So in solution, you can remove a button from Link tag
<Link to="/leadslist">Back to Leads List</Link>
or You can use Below solution,
Instead of adding button in Link You should do this,
import withRouter from 'react-router-dom'
const Component = (props) => {
<div>
<Button onClick={()=>props.history.push('/leadslist')}
</div>
}
export default withRouter(Component)
Add HOC withRouter to your component and you can get history object in prop.
I've decided to use React-Router to manage the routing of my Meteor JS App that uses REACT JS.
I've gotten React-Router to work 'somehow', but it is having some negative side effects that I would like to explain here, in the hopes of finding a solution.
First of all, here is my code:
if (Meteor.isClient) {
Meteor.startup(function () {
console.log('startup');
Hooks.init();
const {Router, Route} = ReactRouter;
const history = ReactRouter.history.useQueries(ReactRouter.history.createHistory)()
React.render((
<Router >
<Route name="home" path="/" component={Sidebar}>
<Route name="managedata" path="managedata" component={ManageData} />
</Route>
</Router>
), document.getElementById("render-target"));
});
}
and in order to navigate between those two routes I've used the Link component provided by React-Router:
<ReactRouter.Link className="item" to="/">
<i className="home icon"></i>
EXAMPLE
</ReactRouter.Link>
<ReactRouter.Link className="item" to="managedata">
<i className="block layout icon"></i>
Manage Data
</ReactRouter.Link>
This is the problem:
1.) Upon loading of the homepage which has the "/" as the path, I am getting random characters showing the URL. This is ugly especially since one would only expect "/" or just nothing to show in the url aside from the domain name or localhost....
e.g. http://localhost:3000/#/?_k=q2c52z
2.) When I click on the Link to 'managedata' I am also getting some random characters in the URL
e.g. http://localhost:3000/#/managedata?_k=6clzfn
Here are my questions:
1.) What is causing those ugly characters to show up in the URL and how do I get rid of them?
2.) Does Link only use the path value? As you can see above, my homepage has a name "home" but a path of "/", I've noticed that if I change the Link to Link to="home" then I get directed to a URL with a "home" on the path which seems to be incorrect.
3.) I ended up using 'component' above as one of the props in Route. I've noticed that many of the examples on the internet uses 'handler'. However, when I use 'handler' in my code.....it doesn't work.....
How are those different?
4.) I've defined my routes as above, but when I use the page does not render initially, but if I click back button on the browser and then click the forward button, then it renders. How do I solve this?
Many hours ago I had the above problems.
Now I've found the solution and I'd like to post it here in the hopes of saving some time for future readers who bump into the same problem.
Here is the Q and A:
1.) What is causing those ugly characters to show up in the URL and how do I get rid of them?
As per https://github.com/rackt/react-router/blob/master/UPGRADE_GUIDE.md
"If you do not specify a history type (as in the example above) then you will notice some unusual behaviour after updating to 1.0.0. With the default hash based routing a querystring entry not defined by yourself will start appearing in your URLs called "_k". An example of how it looks is this: ?_k=umhx1s. "
So add 'history' into the Router.
e.g.
const {Router, Route, IndexRoute} = ReactRouter;
const history = ReactRouter.history.useQueries(ReactRouter.history.createHistory)()
React.render((
<Router history={history}>
<Route path="/" component={Sidebar}>
<IndexRoute component={Dashboard} />
<Route path="managedata" component={ManageData} />
</Route>
</Router>
), document.body);
Notice the history={history} inside the Router.
2.) Does Link only use the path value? As you can see above, my homepage has a name "home" but a path of "/", I've noticed that if I change the Link to Link to="home" then I get directed to a URL with a "home" on the path which seems to be incorrect.
After reading and experimenting....
The value of 'to' should be the path.
Not only should it be the path, it should be the path surrounded by {}.
For example, for the route defined above the Link should be used like this:
<Link to={"/"} className="item" >
<i className="home icon"></i>
EXAMPLE
</Link>
<Link to={"/managedata"} className="item" >
<i className="block layout icon"></i>
Manage Data
</Link>
The surrounding {} made an important difference for me.
Otherwise the page would not render initially....I had to click back and forward again on the browser for the page to render.
3.) I ended up using 'component' above as one of the props in Route. I've noticed that many of the examples on the internet uses 'handler'. However, when I use 'handler' in my code.....it doesn't work..... How are those different?
https://github.com/rackt/react-router/blob/master/UPGRADE_GUIDE.md
Based on the above link, it is the difference between the old API and the new API.
4.) I've defined my routes as above, but when I use the page does not render initially, but if I click back button on the browser and then click the forward button, then it renders. How do I solve this?
I've answered this problem in the answer to number 2.).
But basically, for me, I added {} around the path and it started to render properly.
e.g.
<Link to={"/managedata"} className="item" >
<i className="block layout icon"></i>
Manage Data
</Link>