I've a form with a drop down on it, when I select the drop down I push the value onto the querystring. When the url with that querystring is first hit it stores the querystring param into state and then uses it to populate the drop down. This all works as intended. My issue is triggering the form to see changes to the querystring while still on the same page.
If I'm already on the page but then click a react router Link to the same page but with a different query string parameter it doesn't catch/see that it has changed. I'm wondering how to watch the querystring for changes or if there is a better way to handle this.
I've found that you can listen to the history object that React-Router is meant to use under the hood but I've had little success with this and again I'm not sure if this is the right way to go .md
I've tried adding the following to the page but the listener never seems to fire when I change the querystring (aka search) of the url. Wonder what I'm missing?
useEffect(() => {
// Listen for changes to the current location.
console.info("wiring up history listener");
let unlisten = history.listen(({ location, action }) => {
console.info("HISTORY CHANGED", location.search);
console.log(action, location.pathname, location.state);
// Plan was to update my state here if needed
});
return unlisten;
}, []);
Using react-router-dom
v6.
I've a form with a drop down on it, when I select the drop down I push the value onto the querystring. When the url with that querystring is first hit it stores the querystring param into state and then uses it to populate the drop down. This all works as intended. My issue is triggering the form to see changes to the querystring while still on the same page.
If I'm already on the page but then click a react router Link to the same page but with a different query string parameter it doesn't catch/see that it has changed. I'm wondering how to watch the querystring for changes or if there is a better way to handle this.
I've found that you can listen to the history object that React-Router is meant to use under the hood but I've had little success with this and again I'm not sure if this is the right way to go https://github.com/ReactTraining/history/blob/master/docs/getting-started.md
I've tried adding the following to the page but the listener never seems to fire when I change the querystring (aka search) of the url. Wonder what I'm missing?
useEffect(() => {
// Listen for changes to the current location.
console.info("wiring up history listener");
let unlisten = history.listen(({ location, action }) => {
console.info("HISTORY CHANGED", location.search);
console.log(action, location.pathname, location.state);
// Plan was to update my state here if needed
});
return unlisten;
}, []);
Using react-router-dom
v6.
4 Answers
Reset to default 5If you want to listen for changes on the path's query string parameters you need to "listen" for changes to them from the location object. Use the useLocation hook to get the location
object.
location
{ key: 'ac3df4', // not with HashHistory! pathname: '/somewhere', search: '?some=search-string', hash: '#howdy', state: { [userDefined]: true } }
Listen for changes using effect hook.
const { search } = useLocation();
useEffect(() => {
// search query string changed
}, [search]);
Your code doesn't work because react-router
uses a different instance of history
so your custom listeners aren't fired when the change is made through react-router
's Link
(it gets handled by a different version of history
). However, clicking on the browser's forward or back buttons should trigger your listener since this notifies all history
instances.
The best tool for your use case is useSearchParams
hook
const [searchParams] = useSearchParams();
useEffect(() => {
// code for handling search query change
}, [searchParams]);
Also, you might not need to use useEffect
but treat searchParams
as a source of truth for search data without creating another entity in the state.
react-router comes with all the hooks you need. Sounds to me like you want: useParams
I'm using createBrowserRouter
so I'm using loader
s for all my routes. I've got a child route that has pagination, so when the page is changed, I put the page
query param into the URL so it can be shared. I found the useFetcher
hook and I'm using fetcher.load()
as shown here to trigger the loader
for that route to refresh the data.
I'm updating the page using the useSearchParams
hook then listening for page
to change in a useEffect
where I run fetcher.load()
when the page changes. It works but it feels pretty cumbersome, so if anyone has a cleaner approach, I'm all ears.
useEffect
so it will only ever run on the first render. Also, the return of a useEffect is a callback that will be run before unmounting when a rerender is triggered. – pilchard Commented Jan 10, 2021 at 23:04useHistory
,useParams
, oruseLocation
depending on your needs. They already exist, no need to roll your own – Adam Jenkins Commented Jan 10, 2021 at 23:06