What is the alternative for match in react router dom v6? - javascript

Recently I started upgrading from react-router-dom v5 to v6 and I have something in my Route which I don't know what it is and what is the alternative in react-router-dom v6. The keyword match inside <LayoutReport match={props} /> is giving me warning:
(property) match: any
Type '{ match: any; }' is not assignable to type 'IntrinsicAttributes'.
Property 'match' does not exist on type 'IntrinsicAttributes'.ts(2322)
This is my Route
<Route
path="reports/*"
element={props => (
<FilterBarProvider>
<LayoutReport match={props} />
</FilterBarProvider>)}
/>

It seems you are asking two questions, one about the Typescript error/warning, and the other is an implied "how to access route props in RRDv6" via the title. Answering the second rather resolves the first. In react-router-dom#6 there are no longer any route props. In fact, the element prop takes only a React.ReactNode, a.k.a. JSX, not any callback function that may or may not return JSX.
The route should look something like:
<Route
path="reports/*"
element={(
<FilterBarProvider>
<LayoutReport />
</FilterBarProvider>
)}
/>
This will remove the Typescript error/warning about the match prop that was passed.
But now how to access the old RRDv5 match object? This is easy, use React hooks to access what was previously provided on the match object. For example, if you are trying to access route path params, use the useParams hook to access the params object, i.e. what used to be match.params.
import { useParams } from 'react-router-dom';
const LayoutReport = () => { // <-- remove undefined `match` prop
const params = useParams(); // <-- access params via hook
...
};
There are other hooks to access other route information, so it depends on what you need to access which hook you use.
history object was replaced by a navigate function via the useNavigate hook
location object via the useLocation hook
match object was eliminated, you can access the params via the useParams hook.
Example:
import { useLocation, useNavigate, useParams } from 'react-router-dom';
const LayoutReport = () => {
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
...
};

you can use the useParams hook. https://reactrouter.com/en/v6.3.0/api#useparams

Related

how to listen for route change in react-router-dom v6

am trying to migrate the old react router dom code to v6 and I want to know how to listen for route change, I am now using useHistory
const history = useHistory()
//then
history.listen(...)
I did read the new docs and I did find that useHistory was changed to useNavigate
const navigate = useNavigate()
//then
navigate.listen(...) // listen is not a function
can you please help me find a way to listen to the route change in v6
// This is a React Router v6 app
import { useNavigate } from "react-router-dom";
function App() {
let navigate = useNavigate();
function handleClick() {
navigate("/home");
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}
From documentation (https://reactrouter.com/en/main/hooks/use-location), use this hook
let location = useLocation();
React.useEffect(() => {
ga('send', 'pageview');
}, [location]);
The navigate function is a function, not an object like the older react-router-dom version 5's history object.
You can still create a custom history object but you'll need to create a custom router to use it. This allows you to import your history object and create listeners.
Create a custom router example, use one of the higher-level routers as an example for how they manage the location and state, i.e. BrowserRouter:
const CustomRouter = ({ history, ...props }) => {
const [state, setState] = useState({
action: history.action,
location: history.location
});
useLayoutEffect(() => history.listen(setState), [history]);
return (
<Router
{...props}
location={state.location}
navigationType={state.action}
navigator={history}
/>
);
};
In your code create the custom history object for use by your new custom router and other components. Ensure you have history#5 installed as a project dependency. This is the same version used by RRDv6. If you need to install it run npm i history#5 to add it to the project's dependencies.
const history = createBrowserHistory();
export default history;
Use your router and pass your history object to it.
import CustomRouter from '../CustomRouter';
import history from '../myHistory';
...
<CustomRouter history={history}>
....
</CustomRouter>
In a component you want to listen to location changes on, import your history object and invoke the listen callback as you did previously.
import history from '../myHistory';
...
useEffect(() => {
const unlisten = history.listen((location, action) => {
// ... logic
});
return unlisten;
}, []);
If you want, you may be able to also create your own custom useHistory hook that simply returns your history object.
Update
react-router-dom has started exporting a HistoryRouter for a use case like this. Instead of importing the low-level Router and implementing the internal logic you import unstable_HistoryRouter as HistoryRouter and pass your custom history object (memory, hash, etc).
import { unstable_HistoryRouter as HistoryRouter } from "react-router-dom";
import history from "../myHistory";
...
<HistoryRouter history={history}>
....
</HistoryRouter>
Notes on RRDv6.4+
If you are using RRDv6.4+ and not using the Data routers the good-ish news is that unstable_HistoryRouter is still being exported through at least RRDv6.8.0. You can follow along the filed issue in the repo here.
If you are using the Data routers then the new "unstable" method is to use an attached navigate function from the router object directly.
Example:
import { createBrowserRouter } from 'react-router-dom';
// If you need to navigate externally, instead of history.push you can do:
router.navigate('/path');
// And instead of history.replace you can do:
router.navigate('/path', { replace: true });
// And instead of history.listen you can:
router.subscribe((state) => console.log('new state', state));
I've had mixed results with using the history.listen solution between versions 6.4 and 6.8, so probably best to keep an eye on the linked issue for whatever the RRD maintainers say is the current "unstable" method of accessing the "history".
To add to the accepted answer (can't comment, not enough rep points), subscribing to the history through a useEffect with location.pathname in the dependency array won't work if the navigation unmounts the component you're attempting to call the useEffect from.
If you need to react to a change in the route due to back button specifically:
In react-router-dom v6.8.0 or even earlier, trying to attach a listener to the history, will throw an error: A history only accepts one active listener.
I learnt that react-router-dom seems to introduce a lot of changes between the minor versions as well, so you should take words like unsafe and unstable , like in unstable_HistoryRouter especially serious. They will break sooner or later, if you're not very lucky.
In my case I had to upgrade to get the reintroduced optional route params, and the UNSAFE_NavigationContext my former colleague decided to use, didn't work anymore.
So here's a high level approach, that allows you to listen to the actions on the Router's history stack, without attaching another listener to the router yourself. Which is fine, as it already has one listener by default, and it's just not exposed, but the actions derived from it are, which is enough.
In the following example we are reacting to changes in location and for each change, we check if it was due to a POP action, thats e.g. triggered when the browser's back button is used, and then execute whatever..
import { useEffect } from "react";
import {
Location,
NavigationType,
useLocation,
useNavigationType,
} from "react-router-dom";
export const useBackListener = (callback: () => void) => {
const location: Location = useLocation();
const navType: NavigationType = useNavigationType();
useEffect(() => {
if (navType === "POP" && location.key !== "default") {
if (someCondition === true) callback();
else {
doSomethingElse();
}
}
}, [location]);
};

React Router v6 useNavigate() doesn't navigate if replacing last element in path

I have a react component with the following function
const handleNavigate = (clientId) => {
console.log(clientId)
navigate(`/dashboard/clients/${clientId}`)
}
The console.log() is showing the ID I want to append to use in the navigate function.
AND
The URL in the browser is updating.
But the page does not change.
This works to navigate from /dashboard/clients to /dashboard/clients/foo
but it does not work to navigate from /dashboard/clients/foo to /dashboard/clients/bar
The clientId is passed into the card like so...
const CompanyCard = (props) => {
const { client, showWatchlist, removeDisabled, showRemove, removeType } = props
...
}
then in the card
<CardActionArea
onClick={() => handleNavigate(client._id)}
...
Any ideas?
Thanks
UPDATE
After reading up from #redapollos suggestion I tried Outlet and the
useRoutes methods... neither worked.
import { useRoutes } from 'react-router-dom'
// then in the routes...
{ path: 'clientdetail/:id', element: <ClientDetail /> },
{ path: 'clientdetail/', element: <ClientDetail /> },
This might be due to using the useRoutes hook but I am still working on it.
Another question here on SO that might get an answer sooner -
https://stackoverflow.com/a/70009104/13078911
I just read in React Router Dom Docs v6 this solution:
import { useNavigate } from 'react-router-dom';
...
const navigate = useNavigate();
...
<Button onClick={() => navigate('../user', { replace: true })}>Register</Button>
So, basically I added ../ before the route and the replace: true.
Reference: https://reactrouter.com/docs/en/v6/hooks/use-navigate
It worked for me, hope it works for u! (:
Workable solution!
navigate('/url')
navigate(0)
After replacing the url, manually calling navigate(0) will refresh the page automatically!
This will work in a situation like
navigate from /same_path/foo to /same_path/bar.
Be aware of unintended page referesh behavior:
For a normal situation like navigate from /home to /same_path/bar, navigate(0) will cause page to refresh even after page has finished rendering. And to prevent that, you can take a look at this question.
More info:
useNavigate docs
How do I reload a page with react-router?
Try replacing the URL instead of adding a new one. when you are going from
/dashboard/clients to /dashboard/clients/foo you are going from a parent to a child, your URL has everything plus /foo. But, when you are going from /dashboard/clients/foo to /dashboard/clients/bar you are navigating to a sibling /foo to /bar that might be causing the issue. try to replace the value like navigate(/dashboard/clients/ba, {replace: true}) here is example of how to use this in general. use it for more information. https://www.digitalocean.com/community/tutorials/react-react-router-v6
I had this same issue and my code was fine, however, I found out at this post that optional params aren't supported in v6.
https://github.com/remix-run/react-router/issues/7285
I had:
<Routes>
<Route path="list/:id?" element={<SystemList />} />
</Routes>
and had to change it to:
<Routes>
<Route path="list/:id" element={<SystemList />} />
<Route path="list/" element={<SystemList />} />
</Routes>
I'm hoping they support it in the future but as of v6.0.2 they do not.
Maybe try the component Navigate:
<Navigate to={<your_path>}/>
This might be a little late but I had the same issue and you simply just have to trigger a re-render for your component. You can do this by adding a useEffect hook within your component you have assigned /dashboard/clients/:clientId to with a dependency of the param you want to update.
It should look something like this:
import React, { useEffect } from 'react'
import { useParams, useNavigate } from 'react-router-dom';
const ClientDashboard = () => {
const params = useParams()
const navigate = useNavigate()
useEffect(() => {
//do something with new id
},[params.clientId])
const handleNavigate = (clientId) => {
navigate(`/dashboard/clients/${clientId}`)
}
.....
}
Your page should now update with the new param
I think this is a better work around on this one.
import React from 'react'
import { useHistory } from 'react-router-dom'
...
const history = useHistory()
const handleNavigate = (clientId) => {
console.log(clientId)
history.push(`/dashboard/clients/${clientId}`)
}
Make sure your app is wrapped in a BrowserRouter from react-router-dom
Try adding window-location as key prop to the element in route
ie
<Route path=":id" element={ <Hello key={window.location.pathname} } />
Based on this link https://github.com/remix-run/react-router/issues/8245 I could solve this issue.
I guess that as nothing has been changed the screen is not updated. As soon as I added I useEffect on the target route component and passed the lastelement as a parameter it worked.
In other words: On the target component add: Reac.useEffect(....) [lastElement].

How to properly use useHistory () from react-router-dom?

How to use useHistory() correctly? I can't make the transition from one react component to another.
According to the instructions from the React documentation and also here on Stack Overflow, I cannot make the transition from App.js to MyComponent.js.
For example - I am trying
/* **App.js ** */
/* Import modules */
import React from 'react';
import { useHistory } from 'react-router-dom'; // version 5.2.0
function App()
{
let history = useHistory ();
const handleClick = () => {
history.push ('./pages/MyComponent');
}
return (
<div className="App">
<button onClick={handleClick}>Next page ==></button>
</div>
);
}
I also tested this example, but the output throws the following error when the button is pressed:
TypeError: Cannot read property 'push' of undefined
Does something seem to be leaking to me or is there a mistake on Babel's side?
Project react structure:
+ Root/
+ src/
- App.js
- index.js
+ pages/
- MyComponent.js
This has changed in v6, useHistory is now useNavigate and we can use it as follows:
instead of:
const history = useHistory()
history.push('/')
we now use:
const navigate = useNavigate()
navigate('/')
You can't just use the useHistory hook to redirect to another page.
You need to properly set up your application in order to use React Router. Look at their examples starting from this https://reactrouter.com/web/example/basic
You need to wrap your entire application with <BrowserRouter /> which will give the history object you are looking for through the hook.
By the way, you don't give a relative file path to history.push as an argument, you must give a valid route that you typically setup using <Route /> component
Using history.replace('/<route-name>') also works.
you need to use it with react-router-dom. set your router config and then push it to that path. you can get more information by looking at documentation.
https://reactrouter.com/web/example/route-config
do not forget to set your switch components and your exact for root path.
Using React 17.0>, this works for me:
import { useHistory } from "react-router-dom";
const history = useHistory();
history.push("/home");
I've reached too much to find this correctly use of the useHistory function.
Try and answer this post for feedback.
When you are applying history.push, is that your path name? history.push('/pathname') is the process I guess.
You can see here: https://reactrouter.com/web/api/Hooks

React.js how to access a parameter in a Router Link URL from a function Component?

I had access to params from a router URL by using the following:
${this.props.match.params.id}.
is there a way to access the same param from a functional component as opposed to a class component?
You can use the withRouter hoc provided by react-router :
You can get access to the history object’s properties and the closest 's match via the withRouter higher-order component. withRouter will pass updated match, location, and history props to the wrapped component whenever it renders.
import { withRouter } from 'react-router';
const Component = props => ...
// now you can access props.history|match|location
const ComponentWithRouteProps = withRouter(Component);

How to read the current URL in the react application

I would like read and print the current URL in the react application. Right now i am using "window.location.pathname" to read the URL but would like to know if there is a better way or some react way to read the URL
window.location.href
returns the href (URL) of the current page
window.location.hostname
returns the domain name of the web host
window.location.pathname
returns the path and filename of the current page
window.location.protocol
returns the web protocol used (http: or https:)
if you are using React Router and your component is rendered by a Route like below for example:
<Route exact path='/' component={HomeComponent}/>
that component will automatically receive three objects from Route named history , location and match respectively. by that you can find what you asked under location.pathname. more info here
if you still using react router and your component is not been rendered with Route , you need to use withRouter , which is a HOC and will give you history , location and match as props to your component. more info here
if you are not using react router you gonna need to use window.location.pathname or window.location.href or only location.pathname
If you are using react router:
const currentRoute= this.props.location.pathname
else you can get this like:
const currentRoute= window.location.pathname
href will give you complete url.
We can get it from this.props using withRouter component from react-router-dom package in the following way by adding in the class
import React from 'react';
import { connect } from 'react-redux';
import { Switch, Route, withRouter } from 'react-router-dom';
class Application extends React.PureComponent {
render() {
console.log(this.props)
this.props.location.pathname // we will get the pathname
return (
<div className='application'>
{Hi}
</div>
);
}
}
export default connect(mapStateToProps, actions)(withRouter(Application));
output:
You can use the useParams hook to access values in your URL. When creating your routes in App.jsx you will specify the name of the input variable like this:
<Route
path="/search/:searchType/:module/:category/:search/:source"
exact={true}
component={yourComponent}
/>
Then you can use the useParams hook inside your component to access the variable values.
const {searchType, module, category, search, source} = useParams();
if you using react js then use useLocation and useHistory hooks
import { useHistory ,useLocation } from 'react-router-dom';
const location = useLocation()
console.log(location.pathname)
const history = useHistory()
console.log(history.location.pathname)

Categories

Resources