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.
Related
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}
);
I'm working on a project using next.js. it has a dynamic route using getServerSideProps. You can pass query to this page. folder structure is category/[title]. So it accepts category/[title]?{some query} URL. But when I type it in address bar I receive this error:
This is getServersideProps function (getLotsInCategory() and getCategories() are data fetcher of firebase. They're working in the other pages perfectly):
export async function getServerSideProps(context:any) {
let category:any=[]
let query:any
if(context.query!=undefined){
query=context.query
}
let title=context.params.title;
let lotsInCategory:any=[]
try{
console.log(query)
lotsInCategory=await getLotsInCategory(title)
if(query.subCategory!=undefined){
lotsInCategory=lotsInCategory.filter((el:any)=>query.subCategory.includes(el.subCategory)==true)
}
const categories:any=await getCategories();
category=categories.filter((el:any)=>el.categoryTitle==title)
category=category[0]
lotsInCategory= JSON.stringify(lotsInCategory)
category=JSON.stringify(category)
query=JSON.stringify(query)
}
catch(er){
console.log(er)
}
return {
props: {query,lotsInCategory,category} // will be passed to the page component as props
}
}
I've tried .json() instead of JSON.stringify(), but I received the same error.
At last, I found the real problem. I saw the real error in the terminal in vs code and it was denying permission from firebase. getLotsInCategory and getCategories are my firebase method that works in static pages prefect, But when I use dynamic pages I can't get data from the fire base . so this error solve by changing firebase configs!
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
I've just started using Gatsby with the Sanity headless CMS.
For the most part it's pretty straight forward; but knowing best practises for querying the data through GraphQL is still bothering me. How I'm doing it currently is just frantically clicking through my CMS structure in the GraphQL playground and finding what I want. This works but the lack of uniformity in this approach is making me uneasy.
For example, if I want a hero image that's in the CMS somewhere, i'll need to do something like:
query SomePageQuery($id: String) {
sanitySomePage(id: { eq: $id }) {
id
heroImage {
asset {
fluid(maxWidth: 1500) {
...GatsbySanityImageFluid
}
}
}
}
}
But if I want some PortableText block then I need to query the corresponding _raw field of whatever type. So, if my type was introText, Gatsby also provides a _rawIntroText. I'm only able to get the full PortableText from this _raw version of the data. Like this:
query SomePageQuery($id: String) {
sanitySomePage(id: { eq: $id }) {
id
_rawIntroText
}
}
It seems that, for some data you can use [Type], and sometimes you have to use _raw[Type].
There's not a great deal of documentation as to why this is the case. And I'm not sure if this is enforced via Sanity or Gatsby.
My question I guess would be, why does _raw[Anything] exist in the Gatsby and/or Sanity world, and how do people decide on which to use (other than just trial and error within the GraphQL playground and at runtime)?
This is coming from the gatsby-source-sanity plugin that Sanity built and maintains. Hopefully someone from Sanity can provide more context, but effectively the _raw[FieldName] entries return the original JSON data for the field. The unprefixed field (e.g. fieldName) is probably not what you want—it'll only contain bits of metadata about the data.
I tend to pull the _raw[FieldName] data and then just pass it straight into the #sanity/block-content-to-react component like so:
import React from "react"
import { graphql } from "gatsby"
import SanityBlockContent from "#sanity/block-content-to-react"
export default ({ data: { page } }) => (
<SanityBlockContent
blocks={page.textContent}
projectId={process.env.GATSBY_SANITY_PROJECT_ID}
dataset={process.env.GATSBY_SANITY_DATASET}
/>
)
export const query = graphql`
query SomePageQuery($id: String) {
page: sanitySomePage(id: { eq: $id }) {
textContent: _rawTextContent
}
}
`
Note that I'm using GraphQL aliasing to continue to refer to the field as textContent in my component rather than coupling the component to the specifics of this GraphQL schema.
You don't need to use Gatsby Image for Sanity images since they have their own image transformation pipeline anyways. Instead you can just fetch asset { _id } and then use #sanity/client like this to generate an image url:
import sanityClient from "#sanity/client"
import sanityImageUrl from "#sanity/image-url"
const client = sanityClient({
dataset: process.env.GATSBY_SANITY_DATASET,
projectId: process.env.GATSBY_SANITY_PROJECT_ID,
useCdn: true,
})
const builder = sanityImageUrl(client)
builder.image({ _id: "..." }).width(400).dpr(2).url()
I'm brand new to Apollo and graphql, and I'm trying to setup an e-commerce site with shopify's storefront API. The website is build with react and Next.js for SSR.
I've managed to get some boilerplate code working for basic cart interactions with shopify. I have a Page component that wraps the whole app, and sits below ApolloProvider with access to apollo client. At the moment I'm using compose() to feed my Page component with some graphql (taken from this example):
const pageWithData = compose(
graphql(query), // Query that retrieves base shopify information, such as shop name, description and products
graphql(createCheckout, {name: "createCheckout"}), // Mutation that creates a new checkout object with shopify. Basically a cart object
graphql(checkoutLineItemsAdd, {name: "checkoutLineItemsAdd"}), // Mutation that adds a new lineitem to the checkout object
graphql(checkoutLineItemsUpdate, {name: "checkoutLineItemsUpdate"}), // Mutation that updates a line item
graphql(checkoutLineItemsRemove, {name: "checkoutLineItemsRemove"}), // Mutation that removes a lineitem
)(Page);
This all works as expected, Except when i refresh the browser, the cart is emptied, and a new checkout object is created. So what I want to do is store the checkout ID in localStorage and check if there is an ID in localstorage, before creating a new checkout object. If there is one, i'll load that checkout instead. right now the checkout is created like so in the Page component:
componentWillMount() {
this.props.createCheckout({
variables: {
input: {}
}}).then((res) => {
this.setState({
checkout: res.data.checkoutCreate.checkout
});
});
}
Now, I've found a working graphql query to load an existing checkout based on an ID:
const checkoutFetchQuery = gql`
query checkoutFetch ($checkoutId: ID!) {
node(id: $checkoutId) {
... on Checkout {
webUrl
subtotalPrice
totalTax
totalPrice
lineItems (first:250) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
title
variant {
title
image {
src
}
price
}
quantity
}
}
}
}
}
}
`;
And so I thought I could simply add this to the compose method like this:
const pageWithData = compose(
graphql(query), // Query that retrieves base shopify information, such as shop name, description and products
graphql(checkoutFetchQuery, { name: "fetchCheckout"}), // Query that fetches checkout based on a checkoutID
graphql(createCheckout, {name: "createCheckout"}), // Mutation that creates a new checkout object with shopify. Basically a cart object
graphql(checkoutLineItemsAdd, {name: "checkoutLineItemsAdd"}), // Mutation that adds a new lineitem to the checkout object
graphql(checkoutLineItemsUpdate, {name: "checkoutLineItemsUpdate"}), // Mutation that updates a line item
graphql(checkoutLineItemsRemove, {name: "checkoutLineItemsRemove"}), // Mutation that removes a lineitem
)(Page);
But this results in the following error from the Apollo dev tools:
GraphQL Errors: Variable checkoutId of type ID! was provided invalid value
I'm certain that this is me not understanding some key concept of how compose() work in react-apollo. I understand that I need to feed the query with some variables, but for some reason this query seem to run immediately on load, where as I expected this would simply make the query available on the component. Some of the other graphql() statements expects variables as well, such as "checkoutLineItemsAdd", but this doesn't result in errors. Another thing I've noticed is that the mutations are added as functions to the component props, where as my query is added as an object.
I'm struggling to find any good documentation on this.
Are queries run immediately?
Are mutations waiting to be called from the component, allowing us to dynamically add variables?
Should I write my gql syntax differently for it to become a function on the component instead of an object?
How do we pass variables dynamically to queries, when attached to the compose HOC?
Howcome I get errors from this query, and not the mutations, that also expect variables before running?
Your query requires an input checkoutId which is of type ID.
But your query graphql(checkoutFetchQuery, { name: "fetchCheckout"}) is being fired without any input. You can add the input variables by doing so
graphql(checkoutFetchQuery, {
name: "fetchCheckout",
options: {
variables: {
checkoutId: localstorage.get('checkoutid')
}
}
})
The docs for options config is here
You can also skip a query from autofiring by adding a skip check under options like
options: {
skip: !localstorage.get('checkoutid')
}