warn Non-deterministic routing danger: Attempting to create page: [...] already exists - javascript

I finished writing a small blog application using Gatsby and React.
Everything works fine when I try it locally. So I proceed with gatsby build and deploy the build folder into Netlify. However, after the deployment, the content of some of the pages is not shown despite locally everything works fine.
Description of the problem: I have a navbar with "Home", "Healthcare", "Technology", "Robotics", "Posts", "NewsLetter", and every time a user clicks on for example "Robotics" a series of posts from that category is shown.
Now locally everything works fine but as soon as I deploy I can only see "Posts" page which carries all the posts. The other choices from the navbar are not rendering the other posts categories.
Below the error I am getting from the terminal:
warn Non-deterministic routing danger: Attempting to create page: "/healthcare/", but page "/healthcare" already exists This could lead to non-deterministic routing behavior
warn Non-deterministic routing danger: Attempting to create page: "/robotics/", but page "/robotics" already exists This could lead to non-deterministic routing behavior
warn Non-deterministic routing danger: Attempting to create page: "/technology/", but page "/technology" already exists This could lead to non-deterministic routing behavior
This leads me to think that some pages are not rendered at the proper time, however, this does not explain the difference between localhost perfectly working and the deployed version not working properly.
Below my gatsby-node.js
const path = require('path')
// create pages dynamically
exports.createPages = async ({ graphql, actions }) => {
// from actions we can destructure createPage
const { createPage } = actions
// below we will be running two queries at the same time
// instead of one query only
const result = await graphql(`
{
allMdx {
nodes {
frontmatter {
slug
}
}
}
category: allMdx {
distinct(field: frontmatter___category)
}
}
`)
result.data.allMdx.nodes.forEach(({ frontmatter: { slug } }) => {
createPage({
path: `/posts/${slug}`,
component: path.resolve(`src/templates/post-template.js`),
context: {
slug,
},
})
})
result.data.category.distinct.forEach(category => {
createPage({
path: `/${category}`,
component: path.resolve(`src/templates/category-template.js`),
context: {
category,
},
})
})
}
Below is also the file post-template.js
const PostTemplate = ({data}) => {
const {
mdx: {
frontmatter: {title, category, image, date},
body,},
} = data;
return (
<Layout>
<Hero/>
<Wrapper>
{/* post info */}
<article>
<Image fluid={image.childImageSharp.fluid} />
<div className="post-info">
<span>{category}</span>
<h2>{title}</h2>
<p>{date}</p>
<div className="underline"></div>
</div>
<MDXRenderer>{body}</MDXRenderer>
</article>
{/* banner */}
<article>
<Banner/>
</article>
</Wrapper>
</Layout>
)
}
export const query = graphql`
query GetSinglePost($slug: String) {
mdx(frontmatter: {slug: {eq: $slug}}) {
frontmatter {
title
category
date(formatString: "MMMM Do, YYYY")
image {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
}
body
}
}
`
const Wrapper = styled.section`
// style goes here ...
`
export default PostTemplate
and the file category-template.js
const CategoryTemplate = props => {
console.log(props);
const {
pageContext: { category },
} = props;
const {
data: {
categories: {nodes:posts}
}
} = props;
return (
<Layout>
<Hero/>
<Posts posts={posts} title={`category / ${category}`}/>
</Layout>
)
}
export const query = graphql`
query GetCategories($category: String) {
categories: allMdx(sort: {fields: frontmatter___date, order: DESC}, filter: {frontmatter: {category: {eq: $category}}}) {
nodes {
excerpt
frontmatter {
title
author
category
date(formatString: "MMMM, Do, YYYY")
slug
readTime
image {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
}
id
}
}
}
`
export default CategoryTemplate
Below the structure of the robotics page component (all the other page components healthcare and technology have the same exact structure so I am only including one)
robotics.js
const robotics = () => {
return (
<Layout>
<SEO title="Robotics"/>
<Hero />
<h2>robotics page</h2>
</Layout>
)
}
export default robotics
In order to solve this problem I did a lot of research but before I would like to pint out that I already clean and redeploy the same application a couple of times before. So what I tried so far:
sudo gatsby clean && sudo gatsby develop after it was working I did sudo gatsby build
After deploying the build folder some pages were not rendering properly so I:
sudo m -rf node_modules sudo rm -rf public/ and sudo rm -rf package-lock.json
and procedeed with:
sudo npm install (if necessary you have to do sudo nom install --unsafe-perm) and
sudo gatsby develop && sudo gatsby build
But unfortuantely the same exact outcome: localhost everthing works fine and on Netlify some pages were not rendering at all.
So I did additional research and I studied this post which represents the same exact problem I have. Unfortunately no answer was accepted. But I tried to apply the suggestion and in fact on my gatsby-node.js I had:
exports.createPages = async ({ graphql, actions, reporter }) => {
const yourQuery= await graphql(
`
{
parameters here
}
`
if (yourQuery.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`);
return;
}
But nothing happened.
I studied this and this too. This post says that "MDX pages cant currently be in the same directory as JSX pages" but I am not sure this is accurate and did not see it in the official documentation an example showing not to do that.
Please guide me to a potential solution on this point.

Your issue relies on the naming, and I assume that it will affect all pages (robotics, healthcare, technology, etc). This:
const robotics = () => {
return (
<Layout>
<SEO title="Robotics"/>
<Hero />
<h2>robotics page</h2>
</Layout>
)
}
export default robotics
Should be:
const Robotics = () => {
return (
<Layout>
<SEO title="Robotics"/>
<Hero />
<h2>robotics page</h2>
</Layout>
)
}
export default Robotics
Notice the capitalized component (Robotics). This is because, in React, all component names must be capitalized otherwise, React's assignation will never work.
The rest of the code, without a CodeSandbox it's difficult to check but it looks good.
In addition, something that can also lead to this behavior is the use of plugins such as gatsby-plugin-remove-trailing-slashes, so double-check that if you are using it.
Outside the scope of the question (and because of sudo gatsby develop && sudo gatsby build) it seems that you don't know the difference between gatsby develop and gatsby build, they are not complementary and it doesn't make sense to use that command.
To summarize, gatsby build creates a version of your site with production-ready optimizations like packaging up your site’s config, data, and code, and creating all the static HTML pages that eventually get rehydrated into a React application.
gatsby develop just runs a server in the background, enabling useful features and it is optimized for rapid feedback and extra debugging information. It doesn't bundle and compile your code production environment, potentially hiding some issues that will appear in Netlify, since it runs gatsby build.
With that command, you are developing your site and quitting the process by running the gatsby build, without serving it.
So, to double-check your site before pushing it to Netlify, I would suggest running a gatsby build && gatsby serve locally, since, if the Node versions in Netlify and locally are the same, the project must behave equally in both environments.
You can read the Gatsby docs for further explanations.
I did additional research and I studied this post which represents the
same exact problem I have. Unfortunately no answer was accepted.
I answered that question. The error output may be the same but it's caused but a completely different use-case. In that case, the issue relies on a wrong/unfinished promise approach, and it only affects the dynamic pages, in your case are the opposite. In your scenario, the "static" pages are the problem due to bad naming because React can't resolve the component assignation.

Related

Gatsby Site Requires Refresh to View New Data

I have a blog run on Gatsby, and every time I push and deploy new blog posts I need to do a refresh on my page to see the new data.
I tried following the suggestions from this post and adding an onServiceWorkerUpdate function but that doesn't seem to have done anything.
Anyone have workarounds to this issue, and if so will there be a way to test locally? Changes already automatically update when I test in gatsby develop mode.
This is the entirety of my gatsby-browser.js file
export const onServiceWorkerUpdateReady = () => window.location.reload();
You need to install gatsby-plugin-offline first. Leaving your gatsby-config.js with something similar to:
{
plugins: [
{
resolve: `gatsby-plugin-manifest`,
options: {
...
}
},
'gatsby-plugin-offline'
]
}
Note: plugin's order matters in this case.
The plugin will register a service worker and will load it into the client.
Then, in your gatsby-browser.js file you can simply add:
export const onServiceWorkerUpdateReady = () => {
const answer = window.confirm(
`This application has been updated. ` +
`Reload to display the latest version?`
)
if (answer === true) window.location.reload()
}
Additionally, you may want to add the following hack (very common across the repositories):
export const onRouteUpdate = () => {
navigator.serviceWorker.register('/sw.js').then((reg) => {
reg.update();
});
};
Basically, it forces the registration of the service-worker across the site upon the onServiceWorkerUpdateReady refresh.
Check this interesting thread for further caveats and workarounds for some specific scenarios: https://github.com/gatsbyjs/gatsby/issues/9087#issuecomment-774680408
It will not work using Link provided by gatsby
import { Link as GatsbyLink } from 'gatsby';
<GatsbyLink to={to} {...props} />
It works using traditional a tag:
<a href={props.to} target="_blank">
{props.children}
</a>

Convert string React Component to jsx again

I have one question, because read javascript file from NodeJS and send to the client and here I reveive all file as string (which is obvious) and here is my question. Is any solution to convert string component again to the jsx? This is no only html tags so dangerouslySetInnerHTML or similar methods don't work.
My string components looks like typical React component, something like that:
import React from 'react';
import { Row } from 'react-bootstrap;
import Home from './Home'
......
const Index = () => {
const renderHelloWorld = <h1>Hello World</h1>
return (
<div>{renderHelloWorld}</div>
)
}
export default Index;
So this is string I'm struggling with how convert it to jsx (maybe this is impossible) and I should use Server Side Rendering with React methodfs like ReactDOMServer?
You can use just plain old JavaScript to do the trick.
document.querySelector('#elementToBeReplace').innerHTML = renderHelloWorld;
Another Option with react.js is use of dangerouslySetInnerHTML.
<div dangerouslySetInnerHTML={{ __html: renderHelloWorld }} />
Or You can use html-react-parser.
import Parser from 'html-react-parser';
const renderHelloWorld = <h1>Hello World</h1>
<div>{Parser(renderHelloWorld)}</div>
Try this library https://www.npmjs.com/package/react-html-parser.
A utility for converting HTML strings into React components. Avoids the use of dangerouslySetInnerHTML and converts standard HTML elements, attributes and inline styles into their React equivalents.
So, solution for my problem is very popular and simple (in early project stage), to understand problem and fix it we need to go back to the server part of the app. For React applications if we want render jsx file from the server, we have to use server side rendering and above problem will be gone. Maybe I show step by step how enable rendering React component in te server.
Configure backend to enable the ES6 features
Install babel packages
npm install #babel/core #babel/node #babel/preset-env #babel/preset-react --save-dev
There is many ways to configure Babel, I use the nearest package.json for this.
{
......,
/*This have to be in the top level in package.json*/
"babel":{
"presets":[
"#babel/preset-env",
"#babel/preset-react"
]
}
}
More information about babel packages and configurations: https://babeljs.io/docs/en/
Send React component to the client
For this we have to install react and react-dom packages in server side
npm install react react-dom
For example in your server,js or route.js file:
.... //necesarry imported modules
import ReactDOMServer from 'react-dom/server'
import Index from './Index';
router.get('/exampleRoute', (req, res) => {
.... //your route business logic
res.send(ReactDOMServer.renderToString(<Index/>))
})
Render view in the client
...//correct React component
const[state, setState] = useState({Component: ''});
fetch('/exampleRoute')
.then(response => response.json())
.then(data => setState(state => ({...state, Component: data.data));
....
return(
<div dangerouslySetInnerHTML={{ __html: state.Component }}></div>
)
This is only simple example how you can render React component from backend if this is necasarry. This is not guide for complex server side rendering in application which is more complicated thing, but no difficult thing.

How can I set the index page of my Gatsby site to be one of the dynamically generated pages?

I have a Gatsby site that queries information from a Wordpress REST API with GraphQL to dynamically create the site pages. I'd like to set my index page to be the homepage that is being created dynamically i.e home.html
I saw this post that was similar
On Gatsby CMS how can i set the about page as a index page
However, they have an about.js file that corresponds to their about page, meaning they can export it as a component and use it in index or they can even just copy the contents of that file over to index.js. The homepage that I want to set as my index is being generated dynamically and using a GraphQL query that can't be used outside of the page.js template. So I don't see an easy way to copy that over to another file.
I guess my last option would be to set my server to point to the static file in public/home.html and serve that as the site root, but the person in that posting tries to deter people from doing that.
Any ideas?
Here is page.js template that generates the pages of the site:
const PageTemplate = ({ data }) => (
<Layout>
{<h1 dangerouslySetInnerHTML={{ __html: data.currentPage.title }} />}
{
renderBlocks(gatherBlocks(data.currentPage.acf.page_blocks, data))
}
</Layout>
);
export default PageTemplate;
export const pageQuery = graphql`
query ($id: String!) {
currentPage: wordpressPage(id: {eq: $id}) {
title
id
parent {
id
}
template
acf {
page_blocks {
block_type {
acf_fc_layout
cs_title
cs_text
}
wordpress_id
}
}
}
}
`;
And here is my index page:
import React from "react"
import Layout from "../components/global/Layout"
const IndexPage = () => (
<Layout>
<h1>Hi people</h1>
<p>Welcome to the Tank Gatsby site.</p>
<p>Now go build something great.</p>
</Layout>
)
export default IndexPage
I experienced the same situation today. I used the following approach to use my dynamically created page with uri '/home'(fetched from wordpress using GraphQL query) as the home page of my Gatsby site:
Delete the default index.js file in your pages directory.
In gatsby-node.js file, change the uri
of page from '/home' to '/' just before using the CreatePage API.
Here is the sample code to achieve the desired result:
// loop through WordPress pages and create a Gatsby page for each one
pages.forEach(page => {
if(page.uri==='/home/')
page.uri = '/'
actions.createPage({
path: page.uri,
component: require.resolve(`./src/templates/${page.template.templateName}.js`),
context: {
id: page.id,
},
})
})
In the above code, pages refer to the pages fetched from WordPress using GraphQL.
I could not find an easy way to create index page programmatically. Made it work nonetheless, details below.
createRedirect is valid approach but might affect SEO and definitely affects E2E tests cause actual page content gets rendered with a small delay.
Another thing to consider is that having pages/index.js file is required in order to get index.html file generated on production build. This gets in the way of using createPage({ path: '/', ... cause in my case programmatically created index page was overwritten by the static one (made of pages/index.js). This looks like a bug to me (or rather not supported feature). Corresponding github issue.
looks like deletePage and createPage gatsby-node APIs work asynchronously, hence we have to delete index page created from static file and create the one we want in the same callback. Not 100% sure about this one, but that's my observation.
onCreatePage API is a good candidate since it gets called upon original index page creation and we can take that one out and replace it with the custom one, programmatically created.
There is a catch however - CreatePageArgs interface (unlike CreatePagesArgs) doesn't provide reference to graphql, hence fetching data might be tricky.
Final solution:
export function onCreatePage(args: CreatePageArgs): void {
const { page } = args;
if (page.path === '/') {
const { deletePage, createPage } = args.actions;
const indexPageComponentPath = path.resolve(
'./src/pages/index.tsx',
);
deletePage({
path: '/',
component: indexPageComponentPath,
});
createPage({
path: '/',
component: yourComponentPath,
});
}
}
There is a solution: use createRedirect in gatsby-node.js.
E.g.:
index.tsx
import React from 'react'
export default () => <></>
gatsby-node.js
...
exports.createPages = async ({ actions }) => {
const { createRedirect } = actions
createRedirect({
fromPath: '/',
toPath: '/home',
isPermanent: true,
redirectInBrowser: true,
})
}
...
I was able to address this by copying the contents of the page.js template into index.js , but instead of using a regular GraphQL query, which cannot be used outside of the page template, I used useStaticQuery instead and hardcoded the id of the index page I was retrieving data from.

Nuxt Dynamic Routing on Firebase

My Nuxt.js App has this structure:
/pages/index.vue
/pages/_slug/index.vue
When user gets /{any_page}, it will use the path to build the page content:
/pages/_slug/index.vue
<template>
<div>
{{slug}}
</div>
</template>
<script>
import fetch from 'isomorphic-fetch';
export default {
async asyncData({ params }) {
return { slug: params.slug }
}
}
</script>
This works perfectly when running the Nuxt App directly via yarn dev.
When I try to run this using firebase functions:
$ firebase serve --only functions,hosting
The static routes work perfectly, but the dynamic routes always render the default / page, instead of executing the dynamic one. How do I fix this?
If using nuxt generate, then the explanation below should work. If using nuxt build, look at this repo, it is a template i wrote for nuxt + firebase as ssr.
Generate says it skips over dynamic routes, unless you explicitly specify them, hence 404 errors. In your Nuxt config file, you want to specify those routes. Here is an example of fetching from the database during generation. Note that in my example below, the doc id is the slug for the page.
nuxt.config.js
generate: {
async routes() {
const { db } = require('./services/fireInit');
const qs = await db.collection('recipes').get();
return qs.docs.map(x => `/recipes/${x.id}`);
}
},
The downside to this approach is as you add new items via a CMS, you will need to rebuild/deploy your site each time if you want to be able to use these new links without a 404. CMS without redeploy does work if you navigate to new content 'organically'...404 only occurs on a page refresh or on jumping directly to the page from a hyperlink.

gatsby ecommerce startkit + graphQL query issue

I come from a background of React, Mongo, node, a bit of SQL and Shopify (ton's of JS under my belt).
I came across this JAM stack idea and it seemed interesting and decided to try it out. I run into this problem that frankly I can't seem to wrap my head around after all the GraphQL tut's I've watched and articles I read, I'm clearly missing something important.
Traditionally in REST backend, you develop a scheme and your endpoints and then you ask them for the data.
Following this introduction, I get to the part where I query GraphQL, but I can't understand what or how I'm querying without developing a scheme. Use this code (after setting up Strip with test product / key)
import React from "react"
import { graphql, StaticQuery } from "gatsby"
export default props => (
<StaticQuery
query={graphql`
query SkusForProduct {
skus: allStripeSku {
edges {
node {
id
currency
price
attributes {
name
}
}
}
}
}
`}
render={({ skus }) => (
<div>
{skus.edges.map(({ node: sku }) => (
<p key={sku.id}>{sku.attributes.name}</p>
))}
</div>
)}
/>
)
It states:
You can validate your query and see what data is being returned in GraphiQL, which is available at http://localhost:8000/___graphql when running npm run develop.
Upon visiting this area, I noticed Query setup's and options. Is this where I develop the query that I'm using (or the schema?)
Slightly lost as what this sort of 'connection' looks like.
After following the full tutorial and replacing API keys, I get this error:
GraphQL Error Encountered 1 error(s):
- Unknown field 'allStripeSku' on type 'Query'.
file: /Users/robert/Software/bDev/evu/src/components/products/skus.js
My gatsby-config:
require("dotenv").config({
path: `.env.${process.env.NODE_ENV}`,
})
module.exports = {
siteMetadata: {
title: `Gatsby Default Starter`,
description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
author: `#gatsbyjs`,
},
plugins: [
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
`gatsby-transformer-sharp`,
`gatsby-plugin-stripe`,
{
resolve: `gatsby-source-stripe`,
options: {
objects: ["Sku"],
secretKey: process.env.STRIPE_SECRET_KEY,
downloadFiles: true,
},
},
`gatsby-plugin-sharp`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `gatsby-starter-default`,
short_name: `starter`,
start_url: `/`,
background_color: `#663399`,
theme_color: `#663399`,
display: `minimal-ui`,
icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
},
},
// this (optional) plugin enables Progressive Web App + Offline functionality
// To learn more, visit: https://gatsby.dev/offline
// `gatsby-plugin-offline`,
],
}
I just figured this out as I ran into the same head-scratching issue.
You have to create test products in stripe, not just live products. Make sure you've toggled the Viewing test data toggle in the Stripe admin. Then create your products.
You might need to clear your cache and public folders if it still isn't working.
Hope this helps.
For me the solution was to add STRIPE_PUBLISHABLE_KEY to .env.development and not just STRIPE_SECRET_KEY on its own (which is what the tutorial explicitly says to do). As soon as I'd added both, the build errors went away and the site could load up as normal; I also saw 'allStripePrice' appear as a field in GraphiQL. So the config should take this form:
# Stripe secret API key
STRIPE_PUBLISHABLE_KEY=pk_test_xyz
STRIPE_SECRET_KEY=sk_test_xyz
I've raised this as a documentation issue in the GitHub repo and made Pull Request that addresses it. Feel free to upvote that issue.

Categories

Resources