I'm trying to map params into Next.js getStaticPaths but it doesn't work. Below you can see it works.
But it doesn't work as soon as want to add one more field which is the slug of the article.
The routing looks something like this. index>[username]>[slug] <=== slug is for article.
To simplify the code, the API looks like this.
[
{
id
username
email
},
articles: [
[Object], [Object], [Object],
]
}
]
And inside articles array looks something like this:
articles: [
{
id
title
description
slug
}
]
How do I make it work? How to map username and article's slug to param so that it works?
Edit: I want to have username slug and article slug together so that I can have www.com/[username]/[articleSlug].
I'm not sure I understood your issue very well, but you just use getStaticPaths to generate the article url like this:
export async function getStaticPaths() {
const articles = await fetchAPI("/articles")
return {
paths: articles.map((article) => ({
params: {
slug: article.slug,
},
})),
fallback: false,
}
}
Then to get the user and the article data you can use getStaticProps like this:
export async function getStaticProps({ params }) {
const article = (await fetchAPI(`/articles?slug=${params.slug}`))[0]
const users = await fetchAPI("/users")
return {
props: { article, users },
revalidate: 1,
}
}
Related
I'm using Strapi as a CMS, where I query for slugs, and I would like to have statically generated pages using getStaticPaths and getStaticProps in Next.js.
As I need to work with multiple locales, I have to map through the locales and get paths for each "Announcements" I'm getting from my query.
The error message I'm getting is:
Error: A required parameter (slug) was not provided as a string in getStaticPaths for /updates/announcements/[slug]
This is my getStaticPaths:
export async function getStaticPaths({ locales }: any) {
const paths = await (
await Promise.all(
locales.map(async (locale: any) => {
const { data } = await client.query({
query: gql`
query Announcements {
announcements(locale: "${locale}") {
data {
attributes {
slug
locale
}
}
}
}
`,
});
return {
announcements: data.announcements.data,
locale,
};
})
)
).reduce((acc, item) => {
item.announcements.map((p: any) => {
acc.push({
params: {
slug:
p.attributes.slug === "/" ? false : p.attributes.slug.split("/"),
},
locale: p.attributes.locale,
});
return p;
});
return acc;
}, []);
return {
paths,
fallback: false,
};
}
If I console.log(paths) I get the following in the terminal:
[
{ params: { slug: [Array] }, locale: 'en' },
{ params: { slug: [Array] }, locale: 'en' },
{ params: { slug: [Array] }, locale: 'en' },
{ params: { slug: [Array] }, locale: 'da' },
{ params: { slug: [Array] }, locale: 'sv' },
{ params: { slug: [Array] }, locale: 'nb' }
]
I might think that Next.js don't want the slug to be an array, but I'm not entirely sure. What am I doing wrong?
You page uses dynamic routes named (/updates/announcements/[slug]), therefore the param slug is required in paths.
From the Next.js getStaticPaths documentation:
The paths key determines which paths will be pre-rendered. For example, suppose that you have a page that uses Dynamic Routes named pages/posts/[id].js. If you export getStaticPaths from this page and return the following for paths:
return {
paths: [
{ params: { id: '1' }},
{
params: { id: '2' },
// with i18n configured the locale for the path can be returned as well
locale: "en",
},
],
fallback: ...
}
Then, Next.js will statically generate /posts/1 and /posts/2 during next build using the page component in pages/posts/[id].js.
The slug param can only be a string since it's used to generate routes. As you found when logging paths, you were trying to pass slug: [Array].
The problem in the question's code snippet is this expression to assign a slug:
// ...
params: {
slug: p.attributes.slug === "/" ? false : p.attributes.slug.split("/"), // 👈
},
// ...
This expression will either assign false (boolean) or an array of substrings (see the docs for String.prototype.split()).
In this case, as confirmed in a comment above, simply passing the slug as a string solves the issue.
The confusion likely came from following a tutorial that uses an optional catch-all route (pages/[[...slug]]) instead of regular dynamic routes (pages/[slug]) (ref).
From the Next.js getStaticPaths documentation again:
If the page name is pages/posts/[postId]/[commentId], then params should contain postId and commentId.
If the page name uses catch-all routes like pages/[...slug], then params should contain slug (which is an array). If this array is ['hello', 'world'], then Next.js will statically generate the page at /hello/world.
If the page uses an optional catch-all route, use null, [], undefined or false to render the root-most route. For example, if you supply slug: false for pages/[[...slug]], Next.js will statically generate the page /.
I have several different queries that I need to keep 'live' data from. When I hardcode the query it successfully shows all live changes that happen in the database. But when I pass a payload the data won't change until reloaded.
Working Query:
getOnline: firestoreAction(({ bindFirestoreRef }) => {
return bindFirestoreRef('online', db.collection('units').where('unit.on', '==', true).orderBy('info.timestamp', 'desc').orderBy('unit.status'))
}),
Not working Payload Query: gets data once
getFilterSystemId: firestoreAction(({ bindFirestoreRef} , payload) => {
return bindFirestoreRef('filterBySystemId', db.collection('units').where('unit.group', '==', payload.group).orderBy('info.timestamp', 'desc').orderBy('unit.status'))
}),
I pass the payload simply:
filterSystemId(grouphex){
let payload = {
group: grouphex.toString(),
}
this.getFilterSystemId(payload);
},
How do I pass a payload AND get live updates to any changes that happen in the database?
Ended up using vuefire instead of vuexfire and dynamically binding my queries like this.
const vuefire = db.collection('vuefire')
export default {
components: {},
data() {
return {
//view
vuefire: [],
id: 'true',
user: [],
code: 'true'
};
},
created() {
},
// firestore: {
// vuefire: db.collection('vuefire')
// }
watch: {
id: {
// call it upon creation too
immediate: true,
handler(id) {
this.$bind('user', vuefire.where('a', '==', id))
},
},
Anytime 'id' changes the dataset ('user') is recomputed and accomplishes my goal
I have a page [categories][price].js and im trying to achieve the data structure in getStaticPaths e.g
cat1/10 cat1/20 cat1/30 cat1/40 cat2/10 cat/20 etc
I have looked at this post: Next.js: getStaticPaths for nested dynamic routes as it's the same error but as their data structure is a bit different I'm not sure how to translate this to my example
It looks like im mapping the data incorrectly as I get the following error when trying to create my dynamic routes.
Error: Additional keys were returned from `getStaticPaths` in page "/[catSlug]/[price]". URL Parameters intended for this dynamic route must be nested under the `params` key, i.e.:
return { params: { catSlug: ..., price: ... } }
Keys that need to be moved: 0, 1.
How can I correctly map my data?
[categories][price].js
export async function getStaticPaths() {
const prices = [' 10', ' 20', ' 30']
const categories = [{ name: 'cat' }, { name: 'cat2' }, { name: 'cat3' }]
const paths = categories.map(({ slug }) =>
prices.map((price) => ({ params: { catSlug: slug, price: price } }))
)
return {
paths,
fallback: false
}
}
flatten the array
const paths = categories.map(({ name }) =>
prices.map((price) => ({ params: { catSlug: name, price: price } }))
).flat()
I create my state link with defaults values, something like this:
const stateLink = withClientState({
cache,
resolvers,
defaults: {
quote: {
__typename: 'Quote',
name: '',
phoneNumber: '',
email: '',
items: []
}
}
})
So my cache should not be empty. Now my resolvers map looks like this:
resolvers = {
Mutation: { ... },
Query: {
quote: (parent, args, { cache }) => {
const query = gql`query getQuote {
quote #client {
name phoneNumber email items
}
}`
const { quote } = cache.readQuery({ query, variables: {} })
return ({ ...quote })
}
}
}
The datasource of my resolvers is the cache right ? so I have to query the cache somehow. But this is not working, I guess it is because I am trying to respond to quote query, and for that I am making another quote query.
I think I should get the quote data without querying for quote, but how ?
I am getting this error:
Can't find field **quote** on object (ROOT_QUERY) undefined
Please help
Just wanted to post the same question - and fortunatly just figured it out.
readQuery-Methode only allows you to query from root. So instead you should use readFragment, because it allows you to access any normalized field in the cache, as long you got it's id (Something like this: GraphQlTypeName:0 typically constructed from the fields: id and __typename ). Your Query-Resolver should then look something like this:
protected resolvers = {
Query: {
getProdConfig: (parent, args, { cache, getCacheKey }) => {
const id = getCacheKey({ __typename: 'ProdConfig', id: args.id });
const fragment = gql`fragment prodConfig on ProdConfig {
id,
apiKey,
backupUrl,
serverUrl,
cache,
valid
}`;
const data = cache.readFragment({ fragment, id })
return ({ ...data });
}
}
and the call from apollo like:
let query = this.$apollo.query(`
query prodConfig($id: Int!) {
getProdConfig(id: $id) #client {
apiKey,
backupUrl,
serverUrl,
cache,
valid
}
}`,
{ id: 0 }
);
I'm learning ES6 syntax, as well as the latest Apollo libs. This withData code is adapted from the Githunt-React Apollo demo.
const withData = graphql(GETIMS_QUERY, {
options: ({ params }) => ({
variables: {
"fromID": Meteor.userId(),
"toID": `${params.toID}`,
},
}),
});
params doesn't seem to contain what I expect. I'd like to insert a breakpoint in order to examine the contents of params. But if I add a breakpoint next to options, I find that params is undefined.
I guess I may need to add a breakpoint inside this code block in order to see the contents of params:
const withData = graphql(GETIMS_QUERY, {
options: ({ params }) => ({
//IS THERE A WAY TO ADD A BREAKPOINT IN HERE SOMEHOW?
//MAYBE RETURN `VARIABLES` AS A FUNCTION RESULT?
variables: {
"fromID": Meteor.userId(),
"toID": `${params.toID}`,
},
}),
});
Is there a way to do that?
Thanks in advance to all for any info.
You can call console.log (and you can add a breakpoint on that line) and return the object explicitly:
const withData = graphql(GETIMS_QUERY, {
options: ({ params }) => {
console.log(params);
return {
variables: {
"fromID": Meteor.userId(),
"toID": `${params.toID}`,
},
};
},
});