I am new in Nextjs, i am trying to integrate [slug.js] page, i want to know that how can we manage/get data in sidebar (similar blogs) ? in other words for blog details i used "get static path" and "props", But now i want to pass "current slug" ( to API) so i can fetch all blogs with this blog category,How can i do this ?
Client-side approach:
Since you pass the post as page-props via getStaticProps, you can either take the slug from there (if it's included in your data model), or extract the slug from the url via next's useRouter hook in case you want to do client-side fetching:
import axios from "axios"; // using axios as an example
import { useRouter } from "next/router";
const Component = () => {
const [similarPosts, setSimilarPosts] = useState([]);
const router = useRouter();
const { slug } = router.query;
const getSimilarPosts = async () => {
if (!router.isReady() || !slug) return [];
const { data } = await axios.get("/api/similar-posts-route/" + slug);
return data;
};
useEffect(() => {
if (similarPosts.length > 0) return;
(async () => {
const posts = await getSimilarPosts(); // assuming API returns an array of posts as data.
setSimilarPosts(posts);
})();
}, []);
return <div>Similar posts: {JSON.stringify(similarPosts)}</div>;
};
[...]
Server-Side approach (preferred):
I believe it would be a better approach to directly fetch similar posts inside getStaticProps to reduce API calls and for a better UX.
Inside getStaticProps you can take the slug from context.params and fetch all similar posts directly from your database/CMS, and pass them directly as props to your page component:
export async function getStaticProps({ params }) {
const { slug } = params;
// fetch similar posts directly from the database using the slug (don't call the API, it's not up yet during build phase)
const similarPosts = await executeDatabaseQueryForSimilarPosts(slug);
// [...] fetch the rest of the page props
return {
props: {
similarPosts,
// [...] return the rest of page props
},
revalidate: 60 * 30 // re-fetch the data at most every 30 minutes, so the posts stay up to date
};
}
// directly take all similar posts from props
const Component = ({similarPosts}) => {
return <div>Similar posts: {JSON.stringify(similarPosts)}</div>;
};
Related
I am working in Reactjs and using nextjs,My [slug.js] is working fine with following url
<Link href={`/${post.slug}`}><a>
But i want to send/pass "hidden"(additional parameter) with this,whenever i try to do then i am getting 404 error,I want this because in some page i want to use different api in "serversideprops",Right now here is my code
export const getServerSideProps = async ({ params }) => {
console.log(params); // right now i am getting "slug" as parameter
if(params.anotherparamter)
{
//futher code
}
elseif(params.slug){
const { data: data2 } = await Axios.get(`https://xxxxxxxxxxxxxxxxxxxxxxxxx/${params.slug}`);
}
const blogs = data2;
return {
props: {
blogs: blogs
},
};
};
You can use the as prop to hide the query string.
Your link would look something like this
<Link href={`/${post.slug}?myparam="mysecret"`} as={`/${post.slug}`}></Link> //The link will not show the query param when redirected
You will then be able to access the myparam query in your serverSideProps like so.
export const getServerSideProps = async ({ params, query }) => {
...
const { myparam } = query
console.log(myparam) // will return mysecret as a string
You can read more from the docs
Been experimenting with some server components and nextjs (v13) and have encountered something that i'm not sure how to do. Let's say in theory I wanted to have something with this functionality (with the requests running serverside)
const ClientComponent = () => {
// this value cannot be hardcoded into the server component, nor can we change
// this value at any point in time during the clients components life (just using const for example)
// in reality it'd be set in some other format
const id = "some-id"
return <ServerComponent id={somethingId} />
}
const fetchData = async (id: string) => {
// writing this off top of head, not sure if correct syntax but some basic post request using the id
const res = await fetch("someUrl", { data: id });
const data = await res.json();
return { data };
}
const ServerComponent = async ({ id }: { id: string }) => {
if (!id) return null;
const { data } = await fetchData(id);
return (
<div>
{data.someValue}
</div>
);
}
How would I go about doing something of this nature? is this considered "improper" / "bad practice"? if so what would be a better way to go about doing this? Keep in mind that ClientComponent could be three-four nodes down (with each node being a client component)
Thanks :)
I am using react-query in conjunction with Next JS getServerSideProps to fetch data before a page loads using the hydration method specified in the docs like this:
// Packages
import { dehydrate, QueryClient } from '#tanstack/react-query';
// Hooks
import { useGetGoogleAuthUrl, useGetMicrosoftAuthUrl } from '../hooks/auth';
import { getGoogleAuthUrl, getMicrosoftAuthUrl } from '../hooks/auth/api';
export async function getServerSideProps({ req, res }) {
const queryClient = new QueryClient();
const microsoftAuthQueryClient = new QueryClient(); // Not working
await queryClient.prefetchQuery(['getGoogleAuthUrl'], getGoogleAuthUrl);
await microsoftAuthQueryClient.prefetchQuery(['getMicrosoftAuthUrl'], getMicrosoftAuthUrl); // Not working
return {
props: {
dehydratedState: dehydrate(queryClient),
dehydratedMicrosoftAuthState: dehydrate(microsoftAuthQueryClient), // Not working
},
};
}
export default function Signin() {
const date = new Date();
const { data: googleAuthData } = useGetGoogleAuthUrl();
const { data: microsoftAuthData } = useGetMicrosoftAuthUrl();
console.log(googleAuthData); // logs actual data on mount and data is immediately available
console.log(microsoftAuthData); // logs undefined before eventually logging data after being successfully fetched with the useGetMicrosoftAuthUrl() query
return (
//Page content
);
}
How do I make it work as it is supposed to work. Is it not possible to make multiple requests in getServersideProps using react-query hydration method?
Thank you so much in advance
You would just use the same queryClient and prefetch both queries into it, then hydrate just the one:
export async function getServerSideProps({ req, res }) {
const queryClient = new QueryClient();
await queryClient.prefetchQuery(['getGoogleAuthUrl'], getGoogleAuthUrl);
await queryClient.prefetchQuery(['getMicrosoftAuthUrl'], getMicrosoftAuthUrl);
return {
props: {
dehydratedState: dehydrate(queryClient),
},
};
}
This however fetches them one after the other, so you might want to await them in Promise.all:
await Promise.all([
queryClient.prefetchQuery(['getGoogleAuthUrl'], getGoogleAuthUrl),
queryClient.prefetchQuery(['getMicrosoftAuthUrl'], getMicrosoftAuthUrl)
])
In SingleBlogPost.jsx i have:
export async function getStaticPaths() {
const res = await fetch("http://localhost:1337/api/posts");
let { data } = await res.json();
const paths = data.map((data) => ({
params: { slug: data.attributes.slug },
}));
return {
paths,
fallback: "blocking",
};
}
where I generate blog pages by their slug.
But then in getStaticProps I need to fetch single post by slug but I want to do it by id.
export async function getStaticProps(context) {
console.log("context", context);
const { slug } = context.params;
console.log("slug is:", slug);
const res = await fetch("http://localhost:1337/api/posts");
const { data } = await res.json();
return {
props: {
data,
},
revalidate: 10, // In seconds
};
}
And I want to keep url like /blog/:slug , I dont want to include id. in url .When I already fetch all posts in getStaticPaths how I can access post id in getStaticProps to avoid fetching by slug?
You can filter your API response by your slug to get the same result
const res = await fetch(`http://localhost:1337/api/posts?filters[slug][$eq]${slug}`);
This will generate your desired result
It looks like recently released a workaround using a file system cache.
The crux of the solution is that they save the body object in memory, using something like this:
this.cache = Object.create(null)
and creating methods to update and fetch data from the cache.
Discussion here: https://github.com/vercel/next.js/discussions/11272#discussioncomment-2257876
Example code:
https://github.com/vercel/examples/blob/main/build-output-api/serverless-functions/.vercel/output/functions/index.func/node_modules/y18n/index.js#L139:10
I found a concise work around that uses the object-hash package. I basically create a hash of the params object and use that to create the tmp filename both on set and get. The tmp file contains a json with the data I want to pass between the two infamous static callbacks.
The gist of it:
function setParamsData({params, data}) {
const hash = objectHash(params)
const tmpFile = `/tmp/${hash}.json`
fs.writeFileSync(tmpFile, JSON.stringify(data))
}
function getParamsData (context) {
const hash = objectHash(context.params)
const tmpFile = `/tmp/${hash}.json`
context.data = JSON.parse(fs.readFileSync(tmpFile))
return context
}
We can then use these helpers in the getStaticPaths and getStaticProps callbacks to pass data between them.
export function getStaticPaths(context) {
setParamsData({...context, data: {some: 'extra data'})
return {
paths: [],
fallback: false,
}
}
export function getStaticProps(context) {
context = getParamsData(context)
context.data // => {some: 'extra data'}
}
I'm sure someone can think of a nicer API then re-assigning a argument variable.
The tmp file creation is likely not OS independent enough and could use some improvement.
I'm working on a static website fetching content from the WordPress API.
On the menu of the website, I want the content to be save on a nuxt store, and available on the nav component.
I reed the doc of the nuxt server and the nuxtServerInit action, but I didn't find a nice example of how to make a axion call inside this action, and be able to fetch the store on the component.
I find this, but it's not working .. https://github.com/nuxt/nuxt.js/issues/2307
Thanks a lot for your help.
Try this
store/index.js
export const state = () => ({
data: null
})
export const actions = {
// nuxtServerInit is called by Nuxt.js before server-rendering every page
async nuxtServerInit({ commit, dispatch }) {
await dispatch('storeDispatchFunc')
},
// axios...
async storeDispatchFunc({ commit }) {
const { data } = await this.$axios.get('/api/wp....')
commit('SET_DATA', data)
},
}
export const mutations = {
SET_DATA(state, theData) {
state.data = theData
}
}