Get current page path from react-routers browserHistory object - javascript

Outside of my components I need to query the current active URL. I'm going to set some classes on the body ( which is outside my react root ) based on this.
First attempt was to use
//Gets an array of class names that I can add to my body tag
getClassNames(window.location.pathname);
But it seems window.location.path isn't updated when React Router navigates. Surprising yes.
So I thought, ok maybe I can get this from browserHistory
import { browserHistory } from 'react-router'
But alas, I can't see a way to read the current page path from here either ( no decent API documentation seems to exist for this object)
Any tips? Seems like a simple problem, and it would be if window.location.pathname stayed in sync with the history object.

Ok as of 2017 at least, location is available via
browserHistory.getCurrentLocation();
const location = browserHistory.getCurrentLocation();
console.log(location);

The only way I know to do this right now is to use a listener like
import { browserHistory } from 'react-router'
browserHistory.listen(function(event) {
//manage the pathname on your own
console.log(event.pathname);
});
Not ideal, but even after looking at the DOMUtils library used by history, its implementation of getWindowPath() includes the pathname, search, and hash strings. However, looking for a docs link for you on GitHub, History seems to have received a v3 rewrite as of ~20 days ago and now includes a pathUtils module that may be useful to you.

Related

How can i check pathname is exist in react? [duplicate]

How to check if generic react-router path matches current location pathname?
react-router path: /Movies/:id
location.pathname: /Movies/56fa7446bae6eb301e5937f3
I want to use route paths with menu buttons, to set class="active".
EDIT:
To clarify, paths in my app look like:
/Movies/56fa7/watch
and not like:
/Movies/watch/56fa7
How do I check if the former route is active?
Is it doable without <Link> component?
/Movies/56fa7/watch is arbitrary after /Movies, and <Link> obviously can't be pointed to an arbitrary location. So let's ignore <Link> for a moment:
Is there a standalone function or property in react-router that checks if /Movies/:id/watch is active?
According to the docs, you could use matchPath function which takes two arguments:
pathname you want to match (String).
options (Object) or path (String) to match against.
If matched it will return an object of this shape:
{
path, // the path used to match
url, // the matched portion of the URL
isExact, // whether or not we matched exactly
params
}
Otherwise you'll get null.
To make use of it in your components you could simply do:
import { matchPath } from 'react-router';
// ...
render () {
const isMovieWatchPathActive = !!matchPath(
this.props.location.pathname,
'/Movies/:id/watch'
);
// ...
}
Hope it'll help someone.
As of React Router v4 (March 2017):
I'm a bit late to the party, but hopefully this will help anyone with the same question. When a component is rendered through a Route, certain props are passed to it. These props can be used to determine which route is active.
In a component rendered by a Route, you can use this.props.match.url to get the actual URL requested by the browser, and you can use this.props.match.path to get the path pattern for the current route.
Check working example here: https://codesandbox.io/s/20o7q0483j
Docs related to this are available here:
https://reacttraining.com/react-router/web/api/Route/Route-props
use this
import { matchPath } from "react-router";
const match = matchPath("/users/123", {
path: "/users/:id",
exact: true,
strict: false
});
Check out the Link's property: activeStyle or activeClassName. They are supposed to automatically set the link to active when route matches. See the example: https://github.com/reactjs/react-router-tutorial/tree/master/lessons/05-active-links

HashRouter, base root "/" [duplicate]

Is there a way to force a React-Router <Link> to load a page from path, even when the current location is already that page? I can't seem to find any mention of this in the react-router documentations.
We have a page on a route for "apply" that loads up a landing page with a hero image, some explanatory text, etc., and an "apply for this program" button that swaps in content that acts as an application form. This all happens on the same "apply" route, because users should not be able to directly navigate to this form without first hitting the landing page.
However, when they have this form open, and they click on the apply link in the nav menu again, the entire page should reload as it would on first mount, getting them "back" (but really, forward) to the landing page again.
Instead, clicking the <Link> does nothing, because react-router sees we're already on the "apply" page, and so does not unmount the current page to then mount a different one.
Is there a way to force it to unmount the current page before then mounting the requested page, even if it's for the page users are supposedly already on? (via a <Link> property for instance?)
Note: this question was posted when React-Router meant v5, and while the problem in this post is independent of a specific React-Router versions, but the solutions are not. As such, the accepted answer is the solution for React-Router v6, so if you're still using v5, first and foremost upgrade your version of React-Router, but if you absolutely can't, the accepted answer won't work for you and you'll want this answer instead.
In the Route component, specify a random key.
<Route path={YOURPATH} render={(props) => <YourComp {...props} keyProp={someValue} key={randomGen()}/>} />
when react see a different key, they will trigger rerender.
A fix I used to solve my little need around this was to change the location that React-Router looks at. If it sees a location that we're already on (as in your example) it won't do anything, but by using a location object and changing that, rather than using a plain string path, React-Router will "navigate" to the new location, even if the path looks the same.
You can do this by setting a key that's different from the current key (similar to how React's render relies on key) with a state property that allows you to write clear code around what you wanted to do:
render() {
const linkTarget = {
pathname: "/page",
key: uuid(), // we could use Math.random, but that's not guaranteed unique.
state: {
applied: true
}
};
return (
...
<Link to={linkTarget}>Page</Link>
...
);
}
Note that (confusingly) you tell the Link which values you need pass as a state object, but the link will pass those values on into the component as props. So don't make the mistake of trying to access this.state in the target component!
We can then check for this in the target component's componentDidUpdate like so:
componentDidUpdate(prevProps, prevState, snapshot) {
// Check to see if the "applied" flag got changed (NOT just "set")
if (this.props.location.state.applied && !prevProps.location.state.applied) {
// Do stuff here
}
}
Simple as:
<Route path="/my/path" render={(props) => <MyComp {...props} key={Date.now()}/>} />
Works fine for me. When targeting to the same path:
this.props.history.push("/my/path");
The page gets reloaded, even if I'm already at /my/path.
Based on official documentation for 'react-router' v6 for Link component
A is an element that lets the user navigate to another page by clicking or tapping on it. In react-router-dom, a renders an accessible element with a real href that points to the resource it's linking to. This means that things like right-clicking a work as you'd expect. You can use to skip client side routing and let the browser handle the transition normally (as if it were an ).
So you can pass reloadDocument to your <Link/> component and it will always refresh the page.
Example
<Link reloadDocument to={linkTo}> myapp.com </Link>
At least works for me!
Not a good solution because it forces a full page refresh and throws an error, but you can call forceUpdate() using an onClick handler like:
<Link onClick={this.forceUpdate} to={'/the-page'}>
Click Me
</Link>
All I can say is it works. I'm stuck in a similar issue myself and hope someone else has a better answer!
React router Link not causing component to update within nested routes
This might be a common problem and I was looking for a decent solution to have in my toolbet for next time. React-Router provides some mechanisms to know when an user tries to visit any page even the one they are already.
Reading the location.key hash, it's the perfect approach as it changes every-time the user try to navigate between any page.
componentDidUpdate (prevProps) {
if (prevProps.location.key !== this.props.location.key) {
this.setState({
isFormSubmitted: false,
})
}
}
After setting a new state, the render method is called. In the example, I set the state to default values.
Reference: A location object is never mutated so you can use it in the lifecycle hooks to determine when navigation happens
I solved this by pushing a new route into history, then replacing that route with the current route (or the route you want to refresh). This will trigger react-router to "reload" the route without refreshing the entire page.
<Link onClick={this.reloadRoute()} to={'/route-to-refresh'}>
Click Me
</Link>
let reloadRoute = () => {
router.push({ pathname: '/empty' });
router.replace({ pathname: '/route-to-refresh' });
}
React router works by using your browser history to navigate without reloading the entire page. If you force a route into the history react router will detect this and reload the route. It is important to replace the empty route so that your back button does not take you to the empty route after you push it in.
According to react-router it looks like the react router library does not support this functionality and probably never will, so you have to force the refresh in a hacky way.
I got this working in a slightly different way that #peiti-li's answer, in react-router-dom v5.1.2, because in my case, my page got stuck in an infinite render loop after attempting their solution.
Following is what I did.
<Route
path="/mypath"
render={(props) => <MyComponent key={props.location.key} />}
/>
Every time a route change happens, the location.key prop changes even if the user is on the same route already. According to react-router-dom docs:
Instead of having a new React element created for you using the
component prop, you can pass in a function to be called when the
location matches. The render prop function has access to all the same
route props (match, location and history) as the component render
prop.
This means that we can use the props.location.key to obtain the changing key when a route change happens. Passing this to the component will make the component re-render every time the key changes.
I found a simple solution.
<BrowserRouter forceRefresh />
This forces a refresh when any links are clicked on. Unfortunately, it is global, so you can't specify which links/pages to refresh only.
From the documentation:
If true the router will use full page refreshes on page navigation. You may want to use this to imitate the way a traditional server-rendered app would work with full page refreshes between page navigation.
Here's a hacky solution that doesn't require updating any downstream components or updating a lot of routes. I really dislike it as I feel like there should be something in react-router that handles this for me.
Basically, if the link is for the current page then on click...
Wait until after the current execution.
Replace the history with /refresh?url=<your url to refresh>.
Have your switch listen for a /refresh route, then have it redirect back to the url specified in the url query parameter.
Code
First in my link component:
function MenuLink({ to, children }) {
const location = useLocation();
const history = useHistory();
const isCurrentPage = () => location.pathname === to;
const handler = isCurrentPage() ? () => {
setTimeout(() => {
if (isCurrentPage()) {
history.replace("/refresh?url=" + encodeURIComponent(to))
}
}, 0);
} : undefined;
return <Link to={to} onClick={handler}>{children}</Link>;
}
Then in my switch:
<Switch>
<Route path="/refresh" render={() => <Redirect to={parseQueryString().url ?? "/"} />} />
{/* ...rest of routes go here... */}
<Switch>
...where parseQueryString() is a function I wrote for getting the query parameters.
There is a much easier way now to achieve this, with the reloadDocument Link prop:
<Link to={linkTarget} reloadDocument={true}>Page</Link>
you can use BrowserRouter forceRefresh={true}
I use react-router-dom 5
Example :
<BrowserRouter forceRefresh={true}>
<Link
to={{pathname: '/otherPage', state: {data: data}}}>
</Link>
</BrowserRouter>
Solved using the Rachita Bansal answer but with the componentDidUpdate instead componentWillReceiveProps
componentDidUpdate(prevProps) {
if (prevProps.location.pathname !== this.props.location.pathname) { window.location.reload();
}
}
You can use the lifecycle method - componentWillReceiveProps
When you click on the link, the key of the location props is updated. So, you can do a workaround, something like below,
/**
* #param {object} nextProps new properties
*/
componentWillReceiveProps = (nextProps)=> {
if (nextProps.location.pathname !== this.props.location.pathname) {
window.location.reload();
}
};
To be honest, none of these are really "thinking React". For those that land on this question, a better alternative that accomplishes the same task is to use component state.
Set the state on the routed component to a boolean or something that you can track:
this.state = {
isLandingPage: true // or some other tracking value
};
When you want to go to the next route, just update the state and have your render method load in the desired component.
Try just using an anchor tag a href link. Use target="_self" in the tag to force the page to rerender fully.

ReactRouter: Pass data/object in <Link>

I'm fairly new to React and ReactRouter. I've got basic routing down, and am now trying to pass an object from one component to a sibling component using Link.
I've spent hours and hours here on SO, and viewing other tutorials, but none of the code I've tried works. I either get an error on the receiving component, or no errors, but then the data I'm supposedly sending is undefined. I have a feeling I'm missing something very basic.
Can someone either post a very simple working example of sending an object via Link, and using that data in a sibling component? Or, point to an existing working example on StackBlitz or something similar?
I'm using functional components, React v. 16.13.1, and react-router-dom v. 5.2.0 (version taken from package.json).
Thanks in advance for any and all help!
Here is an example for you:
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useLocation
} from "react-router-dom";
Navigating from Sibling1 to Sibling2:
<Link to={{pathname: "/path_of_sibling2", state: "this data would be sent" }}>Sibling 2</Link>
Or you can pragmatically push like:
history.push({pathname: "/path_of_sibling2", state: "this data would be sent" });
Now in Sibling 2, you can recover this state like this:
const {state} = useLocation();
return (
<p>{state}</p>
);

Programmatic relative links with react-router

Did somebody manage to find a good way how to programmatically navigate to a relative link with react-router?
I've used react-router-relative-links for declarative links, but I can't find a good way to do this programmatically.
I know it could be done manually with resolve-pathname and router.push(…), but that would require access to the location, which is only available on route handler components. My component is somewhat deep down the tree, therefore I'd like to avoid all the wiring back to the top.
Is window.location.pathname the right way to get the location and then use router.push available through withRouter?
Currently I'm using this as a utility function:
import resolvePathname from 'resolve-pathname';
function getPathnameFromRelativeLocation(relativeLocation) {
let {pathname} = window.location;
let basePath = pathname.endsWith('/') ? pathname : pathname + '/';
return resolvePathname(relativeLocation, basePath);
}
And in an event handler of a React component:
// Current path is `/books/123`
let path = getPathnameFromRelativeLocation('write-review');
this.props.router.push(path);
// Current path is `/books/123/write-review`
react-router 3.0 now provides location on the context so it's now easy to just take it from there.
https://github.com/ReactTraining/react-router/issues/3325
if you are using browserHistory
it's simply
browserHistory.push('/yourRelativePath');
for that make sure you have latest react-router and
import { browserHistory } from 'react-router';

How to use js code to forward reactjs-router?

when u seem to require forward in reactjs we used to put a Link or button ,and then consumer click it ,then the page reload corresponding path of router . Now,I want the page turn to other path of router when I request a post and the status of response is 200. It can't use a link or button ,could reactjs use code to do this? like : CertainComponent.setPath(otherPath); CertainComponent.turn;
My English is poor , it may be not clear to delive the function which i want to do.
Thanks!
I think you want to use this.context.router.push('/users/12')
https://github.com/reactjs/react-router/blob/master/docs/API.md#routercontext
First of all the component you are using has to be a container, which means you have to create it with connect() so it has access on store and dispatch:
let MyComponent = ({ dispatch, store }) => {
store.routing.push('url');
}
export default connect()(MyComponent)
And this depends how you named the router key on your store when you combined the routing reducer from redux-router on your store.
There is also another alternative, where you could do
import { hashHistory } from 'react-router';
hashHistory.push('next-url');

Categories

Resources