Why can't I get query params in ```getStaticProps```? - javascript

I will make it simple:
Link: http://localhost:3000/product/bioCloths?activeCategory=medium&id=0
File path: pages/product/[product].js.
Expected: product: "bioCloths, activeCategory: "medium", id: "0"
using getStaticProps at [product].js:
const ProductDetails = (props) => {
console.log("initial props", props);
return <div>product details...</div>
};
export default ProductDetails;
export async function getStaticProps(context) {
return {
props: {
context: context
},
};
}
export async function getStaticPaths() {
return {
paths: [{ params: { product: "1" } }, { params: { product: "2" } }],
fallback: true,
};
}
Props returns: context: params: product: "bioCloths", excluding the query params.
Now if I use the deprecated getInitialProps instead:
ProductDetails.getInitialProps = (context) => {
const activeCategory = context.query.activeCategory;
const id = context.query.id;
const product = context.query.product;
return {
activeCategory: activeCategory,
id: id,
product: product,
};
};
props logs activeCategory: "medium" id: "0" product: "bioCloths"
I need to get these all of these props so I can fetch data before the client mounts.
I understand that getInitialProps is now deprecated, but it works. Why is not getStaticProps working, and should I use it instead of serverSideProps?
This is an ecommerce, and there is no way I can set getStaticPaths for hundreds of possibilities, to work along with getStaticProps so I guess that in this case I should use getInitialProps or getServerSideProps?
P.S - getServerSideProps hits me an error stating that it will only accept .json files.

According to one of the maintainers of Nextjs this is the reply for anyone learning this framework:
getStaticProps generates the page at build time. There's no possible way to know the custom query params your visitors can use at build time.
getInitialProps and getServerSideProps can know all the possible query params because the page is generated at runtime, whenever you receive a new request.
You can learn more about the above here.
"
This discussion can be read on Github

If your page uses a dynamic route, params contain the route parameters. For instance, If the page name is [id].js, then params will look as follows:
{ id: /* something */ }
You can get URL params from inside your getStaticProps or getServerSideProps function with the context argument.
Here’s an example with getStaticProps:
// pages/[id].js
export async function getStaticProps(context) {
const { params } = context;
const id = params.id;
const data = /* Fetching data with the id */
return {
props: data,
}
}
You can do the same with getServerSideProps:
// pages/[id].js
export async function getServerSideProps(context) {
const { params } = context;
const id = params.id;
/* ... */
}

Related

Can't access multiple parameters of getStaticPaths from getStaticProps , Nextjs/Apollo/Graphql

Below you can check the code. I'm trying to use Id to identify single content and use titles for links,
when I console log params.title
console: android-paging-advanced-codelab
but
when I try to console log params.id,
console: undefined
I need to access both params inside the getStaticProps so I can use the exact data I need.
I did try context by passing context and using context.params.id but the result is the same.
read the code below and help me, please!
Here you can see the code of my getStaticPaths :
export async function getStaticPaths(){
const { data } = await client.query({
query: gql`
query {
postContents{
data{
attributes{
post_card{
data{
id
attributes{
TitleForLink
}
}
}
}
}
}
}
`
})
const paths = data.postContents.data.map((item)=> {
return {
params: {
id: item.attributes.post_card.data.id.toString(),
title: item.attributes.post_card.data.attributes.TitleForLink.toString(),
}
}
})
return {
paths,
fallback: false,
}
}
And the code of getStaticProps:
export async function getStaticProps({params}){
const { data } = await client.query({
query: gql`
query {
postCards{
data{
id
attributes{
post_content{
data{
id
attributes{
Title
Description
}
}
}
}
}
}
}
`
})
console.log(params.id)
return {
props: {
content: data.postCards.data,
}
}
}
The getStaticPaths function should return an object with paths parameters that determines which paths will be pre-rendered only, you can pass additional properties like locale if you have i18 configured.
So you need to make another fetching in the getStaticProps function.

react-electron ipcRenderer in useEffect does not rerender

I'm trying to make a global accessible object, so that its values can be changed and can be read from every component. I've create a classs with static fields:
export default class Settings {
static first: string;
static second: string;
}
Lets say I have two components in separate files:
import Settings from './Settings'
// located in firstComponent file
export default function FirstComponent() {
Settings.First = 'test' <---------------- SET VALUE
return (some html)
}
// located in secondComponent file
export default function SecondComponent() {
let qq = Settings.First <----------------------- ASSUME HERE IS VALUE "TEST"
}
But it is not working. How I can create static class/fields that will be accessible within all components like C# static classes. Is it even possible?
Thanks in advance
UPD
Looks like the problem in ipcRenderer:
export default function SettingsEditor() {
const [state, setState] = React.useState({
ipAddress: Settings.ipAddress,
});
useEffect(() => {
electron.ipcRenderer.once('readSettings', (args) => {
console.log('Filling settings');
console.log(args); <------ HERE WE HAVE VALUE like 10.10.10.10
setState({ ...state, ipAddress: args.ipAddress});
console.log(state.ipAddress); <------ UNDEFINED
state.ipAddress = args.ipAddress;
console.log(state.ipAddress); <------ UNDEFINED
Settings.ipAddress= args.ipAddress;
console.log(Settings.gateIpAddress); <------ UNDEFINED
});
electron.settingsApi.read();
}, []);
How I can handle this?
The reason is - I'm stupid =)
When I save settings I did it like this:
const settings = new Settings();
settings.ipAddress= state.ipAddress;
console.log(JSON.stringify(settings));
electron.settingsApi.save(settings); <------ PASS OBJECT
But when I return response it was:
ipcMain.on('readSettings', (event, _) => {
storage.getAll((err: Error, data: object) => {
if (err) {
return;
}
const { appSettings } = data;
const settings = new Settings();
settings.ipAddress= appSettings.ipAddress;
console.log('reading settings');
console.log(JSON.stringify(settings));
event.reply('readSettings', JSON.stringify(settings)); <-------- FACEPALM
});
});
What I can say - genius

Nextjs getStaticProps not fired

My [slug].js file, below, has two nextjs helper functions. The getStaticPaths and getStaticProps are exported. In my case, it creates the path posts/[slug]. There is one post file added called hello.json. Now when I navigate to localhost:3000/posts/hello it errors saying:
TypeError: Cannot read property 'fileRelativePath' of undefined. For line 10.
This makes sense after seeing that jsonFile is undefined. As a matter of fact, the whole getStaticProps is never called, the log in there is never logged. Why is this happening?
Thanks in advance.
import React from 'react';
import glob from 'glob';
import { usePlugin } from 'tinacms';
import { useJsonForm } from 'next-tinacms-json';
const Page = ({ jsonFile }) => {
console.log(121212, jsonFile);
// Create the tina form
const [post, form] = useJsonForm(jsonFile);
// Register it with the CMS
usePlugin(form);
return (
<h1>
{post.title}
</h1>
);
};
export default Page;
/**
* By exporting the async function called getStaticProps from a page, Next.js
* pre-renders this page at build time using the props returned by
* getStaticProps.
* The getStaticPaths function defines a list of paths that have
* to be rendered to HTML at build time.
*/
export async function getStaticProps({ ...ctx }) {
console.log(1212, ctx);
const { slug } = ctx.params;
const dynamicPath = `../../posts/${slug}.json`; // for eslint parsing error: "Cannot read property 'range' of null Occurred while linting"
const content = await import(dynamicPath);
console.log(121212, content);
return {
props: {
jsonFile: {
fileRelativePath: `/posts/${slug}.json`,
data: content.default,
},
},
};
}
export async function getStaticPaths() {
//get all .json files in the posts dir
const posts = glob.sync('posts/**/*.json');
const paths = posts.map(file => ({
params: {
slug: `${file.replace('.json', '')}`,
},
}));
return {
paths,
fallback: true,
};
};
After some more digging around I found the issue, posting here to hopefully help future readers with the same issue.
The culprit was this:
const dynamicPath = `../../posts/${slug}.json`; // for eslint parsing error: "Cannot read property 'range' of null Occurred while linting"
const content = await import(dynamicPath);
Using a variable in a dynamic import does not work, only strings or template literals. I used a variable because of a eslint parsing error that can only be resolved by downgrading to an earlier version of eslint. This causes eslint to not work for me in this file, but okay, at least the function is called.
This combined with the observation that the component code is called before the getStaticProps is called made the jsonFile variable undefined and the whole component erroring before it'll ever reach the getStaticProps. You can see that the log starting with 121212 is coming in earlier than 1212. Terminal logs:
121212 {
fileRelativePath: 'posts/hello.json',
data: { title: 'Not the actual data' }
}
1212 hello
This is counter intuitive to me as I figured it would first get the props and pass them to the component immediately, but sadly, defining default props is needed to work around this.
New code:
import React from 'react';
import glob from 'glob';
import { usePlugin } from 'tinacms';
import { useJsonForm } from 'next-tinacms-json';
const Page = ({ jsonFile }) => {
console.log(121212, jsonFile);
// Get content and form for Tina
const [content, form] = useJsonForm(jsonFile);
// Register it with the CMS
usePlugin(form);
return (
<h1>
{content.title}
</h1>
);
};
Page.defaultProps = {
jsonFile: {
fileRelativePath: 'posts/hello.json',
data: {
title: 'Not the actual data',
},
},
};
export default Page;
/**
* By exporting the async function called getStaticProps from a page, Next.js
* pre-renders this page at build time using the props returned by
* getStaticProps.
*/
export async function getStaticProps({ params: { slug } }) {
console.log(1212, slug);
// This line caused the issue
// const dynamicPath = (`../../posts/${slug}.json`; // for eslint parsing error: "Cannot read property 'range' of null Occurred while linting"
const content = await import(`../../posts/${slug}.json`);
return {
props: {
jsonFile: {
fileRelativePath: `posts/${slug}.json`,
data: content.default,
},
},
};
}
/**
* The getStaticPaths function defines a list of paths that have
* to be rendered to HTML at build time.
*/
export async function getStaticPaths() {
//get all .json files in the posts dir
const posts = glob.sync('posts/**/*.json');
return {
paths: posts.map(file => ({
params: {
slug: `${file.replace('.json', '')}`,
},
})),
fallback: true,
};
}

String interpolation is not allowed in graphql tag. (Gatsby)

Attempting to use an alias for a long complicated Ids within my graphql query:
FAILED TO COMPILE: String interpolation is not allowed in graphql tag:
const query = graphql`
query MyQuery {
wordpress {
menu(id: "${wordpress("mainMenu")}") {
...rest of query
}
}
}
`
You should use query variable instead.
Variables can be added to page queries (but not static queries) through the context object that is an argument of the createPage API. docs
// inside template file
export const query = graphql`
query MyQuery($id: String!) {
menu(id: { eq: $id }) {
...rest of query
}
}
`
Then you can provide such variable as part of the page’s context in gatsby-node.js. For example:
// gatsby-node.js
const postTemplate = path.resolve(`./src/templates/post.js`)
allWordpressPost.edges.forEach(edge => {
createPage({
path: `/${edge.node.slug}/`,
component: slash(postTemplate),
context: {
id: edge.node.id, // 👈
},
})
})
Have a look at this using-wordpress example from gatsby.

Pass object to query on Router.push NextJs

Im new to NextJs and im trying to pass an object to another page through a component. I'll post the code and explain better what im trying to do:
The object is like this:
objectEx = {
name: 'name',
description: 'description'
}
This is the main component:
Im trying to pass the object in the Router.push
export default class ComponentDummy extends Component {
handleSubmit = value => e => {
e.preventDefault()
Router.push({
pathname: '/OtherPage'
query: { **PASS AN OBJECT HERE** } //what im trying is: query: { objectEx: objectEx},
}
}
}
This is the page that im trying to receive the object in query
const OtherPage = ({ query }) => {
return(
<div>
Do Stuff with the object here
</div>
)
}
OtherPage.getInitialProps = ({ query }) => {
return { query }
}
Then on the page above im trying to access the object like:
query.objectEx.name
This is not working as I thought I would. How can I achieve that?
Thanks in advance
Well, first thing is you passed object as a query param.
Router.push({
pathname: '/OtherPage'
query: { data: objectEx}
}
So, in order to access the object in the OtherPage, you need to fetch this inside componentDidMount.
componentDidMount() {
let data = window.location.search;
console.log(data)
}
You are not allowed to pass Object as query param for next/router. You need to pass string
Router.push({
pathname: '/OtherPage'
query: { objectEx: JSON.stringify(objectEx) },
}
Keep in mind, that you'll need to parse it your self
const params = JSON.parse(decodeURIComponent(router.query.objectEx))
Better yet to use lib like qs for stringifying objects to URL params.

Categories