Hi there,
New to reach-router, i'd like to match some query parameters to component props like this:
Browser route:
http://localhost/products/view?fullscreen&pageSize=10
Route Component to get:
<MyRouteComponent fullscreen pageSize="10"/>
Is it already somehow possible with reach-router and if yes then how?
Thank you in advance,
Lex
Hey there, @demigor ! I was trying to figure out the same as you, but as I'm also new here, please take what I say with a grain of salt.
The location prop passed down to RouteComponents is an object that contains a search field. This search field contains all the query parameters (query strings) from your URL. In your example, /products/view?fullscreen&pageSize=10 would return location.search: ?fullscreen&pageSize=10, which is a query string.
In order to work with this query, you can use the simple query-string library as the following example:
import * as queryString from 'query-string';
...
// inside MyRouteComponent
export const MyRouteComponent = props => {
const queryParams = queryString.parse(props.location.search);
/* queryParams: {
fullscreen: null,
pageSize: 10,
}
*/
...
}
Go ahead and do a few experiments and you'll easily get the hang of it! Any questions lemme know :smile:
PS: @ryanflorence I think the documentation is lacking in this regard as I believe lots of newbies like me don't necessarily understand the location prop... lemme know what you think, I'd be glad to help!
You can also supply a custom history created by qhistory package to include location.query field with parsed params object:
import { createHistory, LocationProvider } from '@reach/router'
import qhistory from 'qhistory'
import { stringify, parse } from 'qs'
const history = qhistory(
createHistory(window),
stringify,
parse
)
export const App = () => (
<LocationProvider history={history}>
{<your app here>}
</LocationProvider>
)
@Hypnosphi I am having issues with navigation if Router component is inside the location Provider. How did you set it up?
qhistory and reach history have different APIs so my actual code looks like that:
import createBrowserHistory from 'history/createBrowserHistory'
import qhistory from 'qhistory'
import { stringify, parse } from 'qs'
const history = qhistory(createBrowserHistory(), stringify, parse)
const historySource = {
get state() {
return history.location.state
},
pushState: (state, _, to) => history.push(to, state),
replaceState: (state, _, to) => history.replace(to, state),
}
const UnlistenMap = new WeakMap()
const reachHistory = createHistory({
addEventListener(event, listener) {
if (event === 'popstate') {
UnlistenMap.set(listener, history.listen(listener))
} else {
window.addEventListener(...arguments)
}
},
removeEventListener(event, listener) {
if (event === 'popstate') {
const unlisten = UnlistenMap.get(listener)
if (unlisten) {
unlisten()
}
} else {
window.removeEventListener(...arguments)
}
},
get location() {
return {
...window.location,
...history.location,
}
},
set location(value) {},
history: historySource,
})
export const App = () => (
<LocationProvider history={reachHistory}>
<Router>
{<my routes>}
</Router>
</LocationProvider>
)
For a SSR setting, you might need two things:
props into PageContextuseQueryParams hook for deep children to get props from page componentpageContext.js:
import React, { createContext } from 'react'
const pageContext = createContext()
export const withPageContext = Component => props => {
return (
<pageContext.Provider value={{...props}}>
<Component {...props} />
</pageContext.Provider>
)
}
export default pageContext
useQueryParams.js
import pageContext from 'utils/pageContext'
import {useContext} from 'react'
import { parse, parseUrl } from 'query-string'
const isServer = typeof(window) === 'undefined'
const useQueryParams = () => {
const {location} = useContext(pageContext)
const queryParams = isServer ?
parseUrl(location.pathname).query :
parse(location.search);
return queryParams
};
export default useQueryParams;
You need to wrap route page component with withPageContext then you're good to use useQueryParams on children.
Hope this helps.
@revskill10 I added this, but clicking on back button doesn't render the Components again. Any solution for that?
Going off of the OP and seeing that they were correctly directed to destructure the location object.
There's also a few previous issues that suggest documenting how what the location object would provide some good value, so I'll go ahead and close out this issue.
Thanks for helping to make reach router a better router!
Hook based on useLocation:
import { useLocation } from "@reach/router";
import qs from "qs";
export default () => {
const location = useLocation();
const queryParams = qs.parse(location.search, {
ignoreQueryPrefix: true,
});
return queryParams;
};
Most helpful comment
Hey there, @demigor ! I was trying to figure out the same as you, but as I'm also new here, please take what I say with a grain of salt.
The
locationprop passed down toRouteComponents is an object that contains asearchfield. This search field contains all the query parameters (query strings) from your URL. In your example,/products/view?fullscreen&pageSize=10would returnlocation.search: ?fullscreen&pageSize=10, which is a query string.In order to work with this query, you can use the simple query-string library as the following example:
Go ahead and do a few experiments and you'll easily get the hang of it! Any questions lemme know :smile:
PS: @ryanflorence I think the documentation is lacking in this regard as I believe lots of newbies like me don't necessarily understand the
locationprop... lemme know what you think, I'd be glad to help!