How to pass multiple queries in next router - reactjs

I am working in next.js and next-router
I have 2 data parameters that I want to pass
One is entity_id and the other is url_key.
data={
entity_id: 5,
url_key: 'canada/ontario/store_five'
}
Currently I am able to pass one url_key:
Router.push('/store?url_key=' + marker.url_key, `/store/${marker.url_key}`)
The URL is appearing just as I wanted like
http://BaseUrl/store/canada/ontario/store_five
Now I want to also send entity_id along with above url_key but that should not display in URl

You can pass as many query params as you want, it just using query-string.
// using urls
Router.push(
`/store?url_key=${marker.url_key}&entity_id=${marker.entity_id}`,
`/store/${marker.url_key}`
);
// using object
Router.push({
pathname: '/store',
query: { url_key: marker.url_key, entity_id: marker.entity_id },
asPath: `/store/${marker.url_key}`,
});
For more info, read router docs

I would suggest you use a query object to pass multiple queries in next router. Using package
import {useRouter} from "next/router";
const router=useRouter();
router.push({
pathname:'/store',
query:{entity_id :"2221ACBD",url_key:"URL KEY"},
})
To fetch the data from the query you can use array destructuring of query like this :
const { query } = useRouter();
console.log("query::",query);
console.log("entity key:-",query.entity_id);
console.log("url_key:-",query.url_key);
Example : Example1

Related

How to add URL args depending on content in NextJS React web app? [duplicate]

I am developing a ecommerce store using NEXT.JS and Redux. So in product listing page, I have sorting select dropdown with Price Low to High, Price High to Low and New Arrivals. Upon selecting this option, I want to change the URL without page refresh and API call should occure. I have tried using below code, but it is not working and page is reloading.
function sortBy(value) {
router.replace({
pathname: '/products/'+slug,
query: { sort: value }
})
dispatch(fetchproducts(slug, sort));
}
The above code just refresh the current page and appending sort param to URL.
So is it possible to do it without page refresh like in Flipkart.
With the help of shallow-routing change of URL without doing a page reload is possible. It can be enabled by passing explicit option object as third argument to Router.push, i.e { shallow: true }
From the docs
Shallow routing allows you to change the URL without running data fetching methods again, that includes getServerSideProps, getStaticProps, and getInitialProps.
You'll receive the updated pathname and the query via the router object (added by useRouter or withRouter), without losing state.
For example, this how you would update the query param sortBy for pathname /products with the help of shallow-routing.
Router.push({
pathname: '/products',
query: { sortBy: 'price' }
},
undefined, { shallow: true }
)
But there are a few caveats It is not possible to do shallow-routing between different pages, it works only for same page URL changes. See the caveat section for more details.
For example, you can update a query param for page /product page, but it won't be possible if you try to do shallow-routing from /product to /product/[slug] because they are two distinct pages.
// page will reload because shallow-routing not possible between the pages
Router.push('/product', '/product/some-product?sortBy=price', { shallow: true })
Example:
you have a folder like: posts/[id].js and your url is like http://something.com/posts/123
You want to add a query param that will not refresh the page and your url will be like: http://something.com/posts/123?param=ok
all you need to do is:
const postId = 123;
const param = 'ok';
router.push(
{
pathname: `/posts/[id]`,
query: {
postId,
param
}
},
`/posts/${postId}?param=${param}`,
{shallow: true}
);

How to access data passed from a component to a page via <LInk> in nextjs

I have a nextjs app . I have used this answer https://stackoverflow.com/a/72221419/14287707 to pass data from a component to a page using the Link like this.
<Link href={{
pathname: "/products/"+id,
query: userId // the data
}}>
<a>Show more</a>
</Link>
So I am able to see the extra data that I have passed on the receiving page as an object like this,
[id].tsx
const router = useRouter();
const data = router.query;
console.log("USER ID IS!!!!!!", data)
but the console log gives me the value with an empty key like this
{990:""
id: "12"}
It also gets appended on the url like this
products/12?990
How can I access the 990 ? eg I can get the id like this data.id . But what about the userId ?
Your current url is products/12?990. It should ideally be products/12?userId=990. The reason for that is that you are passing a primitive string like query: userId, which translates to query: 990.
You are supposed to pass an object according to the docs like :
query: { userId : userId }
or simply:
query: { userId }

Does Gatsby's createPages API send the path to the template?

I've been following this Gatsby tutorial to create pages dynamically based on my CMS's creation of markdown files: https://www.gatsbyjs.org/docs/adding-markdown-pages/
I don't understand how the GraphQL query in the file blogTemplate.js receives the $path variable. I can see that the createPages function in gatsby-node.js creates the page based off the result of its own GraphQL query, and uses the 'path' frontmatter element to choose the URL for the created page.
However, how does that created page know its own path? The query called pageQuery in the tutorial uses the $path variable to source its own data, but I can't see how it receives that variable. Thank you in advance for any explanation.
While creating the pages, we can pass context,
All context values are made available to a template’s GraphQL queries as arguments prefaced with $
exports.createPages = async function ({ actions, graphql }) {
const { data } = await graphql(`
query {
allMarkdownRemark {
edges {
node {
fields {
slug
}
}
}
}
}
`)
data.allMarkdownRemark.edges.forEach(edge => {
const slug = edge.node.fields.slug
actions.createPage({
path: slug,
component: require.resolve(`./src/templates/blog-post.js`),
context: { path: slug }, //here you can pass path through context parameter, the slug can be then accesed under $path variable in the template
})
})
}
`
using $ sign we can then access the path value in template side
export const query = graphql`
query($path: String!) {
...
}
`
Graphql uses redux internall and all information regarding pages creation and paths are updated to gatsby's redux store and from thereon the graphql query is executed for each page
Now according to gatsby createPage github code comment
Data in "context" is passed to GraphQL as potential arguments when
running the page query.
When arguments for GraphQL are constructed, the context object is
combined with the page object so both page object and context data
are available as arguments. So you don't need to add the page "path"
to the context as it's already available in GraphQL. If a context
field duplicates a field already used by the page object, this can
break functionality within Gatsby so must be avoided.

How do I add a query param to Router.push in NextJS?

With NextJS, I'm trying to route to another page using an object that has a to and as field:
export const routes = {
'BrowseList' : {
'to' : '/apps/Browse/list',
'as' : '/browse/list'
}
// ....
}
and then that's imported and used like so:
import { routes } from './__routes';
import Router from 'next/router';
// ....
const { to, as } = routes.BrowseList;
Router.push(to, as);
which all works. My dilemma is that I'm trying to do something similar to this while attaching a query param. I'm trying to follow this example according to the docs:
Router.push({
pathname: '/about',
query: { name: 'Zeit' },
})
What I've tried (which doesn't work):
Router.push({
pathname : to,
as,
query : { user_id: this.props.data.member.user.id },
});
which gives me a console warning of
Unknown key passed via urlObject into url.format: as
I know I can maybe possibly just use string interpolation and do something like this:
Router.push(to, `${as}?user_id=`${this.props.data.member.user.id}`)
but I was wondering if there was something I'm missing in the doc's example that also adds the query param into my as value.
Thank you.
You were close #nyphur. The as value goes as the second parameter of push and not inside the object that corresponds to to (check router.d.ts to see how push is defined). That's why you're getting the error Unknown key passed via urlObject into url.format: as. After 10 months from your question maybe this could still be useful to someone looking for an answer. Assuming you have a way to build the query string for the as parameter, following #Gezim answer or by any other approach:
Router.push({ pathname: to, query: { user_id: this.props.data.member.user.id } }, as, options);
NOTE: Based on #Gezim answer, if you format the string or pathname in the first parameter to contain your query params, it'll work BUT encoded values, if any, like %2B for instance will be decoded so you will get +. This doesn't happen if the query params object go inside query. Consider this if you have any kind of logic that depends on this.
It appears that the router in next.js doesn't have any convenient API to navigate to using a query string.
I created a utility class called LinkCreator with a toQuery method as follows. It uses query-string to create the query string:
import * as qs from 'query-string';
export class LinkCreator {
static query(object) {
return qs.stringify(object);
}
static toQuery(object, path = "/") {
const query = this.query(object);
return path + '?' + query;
}
}
Then, it can be used with Router.push like so:
Router.push(LinkCreator.toQuery({ name: 'Zeit' }), '/about');
Edit: at first I thought merging an object via spreading would be an easy fix, but then as a comment pointed out there needed to be some changes, so I have updated my answer to still utilize spreading, but unfortunately it does now make the Routes more complicated and involved, but the consumption of it is still straight forward.
I would also freeze the Routes object for peace of mind as well.
import Router from 'next/router';
export const Routes = Object.freeze({
BrowseList(query) {
return [
{
pathname: '/apps/Browse/list',
query
},
'/browse/list'
]
}
// ....
})
Router.push(
...Routes.BrowseList({
paramName: "Param value here"
})
)
Additional Abstraction
import Router from 'next/router';
const QueryRoutePath = (to, as, query) => ([
{
pathname: to,
query
},
as
])
export const Routes = Object.freeze({
BrowseList(query) {
return QueryRoutePath(
'/apps/Browse/list',
'/browse/list',
query)
}
// ....
})
const query = {
paramName: "Param value here"
}
Router.push(
...Routes.BrowseList(query)
)

React Router: Handling queries through React router

So, currently, I have a routing component:
<Route path="/lists/:query" component={Lists} />
I get a call like:
http://localhost:4567/lists/page=17&city_codes=2567
In my Lists component, I handle this query in this way:
componentDidMount() {
const query = match.params.query;
const cleanQueryString = query.replace(/[|;$%#"<>()+,]/g, '');
// break up the string using '&' and '=' into an object
const properties = this.queryURL(cleanQueryString);
const cleanQueryObj = _.pick(Object.assign({}, ...properties), [
'page',
'city_codes',
'min_monthly_fee',
'max_monthly_fee',
'order_by',
]);
// update the query object based on component state
this.setState({ query: cleanQueryObj }, () => {
cleanQueryObj.page && this.updateIndex(parseInt(cleanQueryObj.page, 10));
});
// call axios request and update redux
dispatch(handleLists(cleanQueryObj));
// update browser url
this.props.history.push(cleanQueryObj);
Now, I see a lot of major sites using ?q= before the query and I'm wondering what I'm missing or what could be improved?
Thoughts?
While what you are doing is technically valid, it is a bit non-standard. The way you use the router :query param and the way it is formatted, reaaaaly looks like an actual location.search parameter format, and not a path parameter.
A more standard way to do it, would be with the following URL:
http://localhost:4567/lists?page=17&city_codes=2567
And code as follow:
// In your routes, just a simple route with no path params
<Route path="/lists" component={Lists} />
// In your component
import queryString from 'query-string'
[...]
componentDidMount() {
// Use location object from react-router
const { search } = this.props.location
// Parse it using a npm dedicated module
const { page, city_codes } = queryString.parse(search)
// Now you can use those params
]);
Edit: and now an actual answer to the question:
?q=blah is usually used in a search context, with q parameter being a string used to search something. There can be other parameters following for example ?q=blah&ext=txt.
It is hence different from your :query path param, which is encoded to contain multiple parameters, while q here is a single ready-to-use parameter.

Resources