Next.js change url without building the page using ISR - next.js

I am using ISR to build static product pages using next.js. Since there are a lot of product page to generate I only generated a few pages for it. The problem that I am trying to solve is the delay in transferring the view to the product page.
So I have a category page and within it have a list of products. On each product card item, I use next.js link so that the user can go to the product page.
The problem here is the delay going to the product page when the page is not yet generated. Going to the product page is slow because next.js is building the page. I want to transfer the user to the product page immediately while showing the loading state of the page via the router.isFallback condition.
What I'm trying to achieve is the same as what a normal link would do because it shows the loading state of the page but I don't want to reload the page.

Instead of using next/link or router.push, use router.replace
router.replace(`/product/${id"}`)
Let me know if this work.

What you could do is to make the props not required,
The thing that must take time during the loading of you're ISG page of nextJs is the call api in GetStaticProps,
Something like that:
export async function getStaticProps({ params }) {
const { status, data } = await axios.get<Data>(
`${server}/data`
);
if (status === 404) {
return { notFound: true };
}
return {
props: {
...data
},
revalidate: 60,
};
}
But you could also decide that you will fetch the data during the loading of the state with a fall-back blocking:
const MyPage = (props) => {
const [data,setData] = useState<Data>(null);
useEffect(() => {
(function()
const {data} = axios.get(`${server}/data`);
setData(data);
)()
},[])
return(
<div>
!data ? <div>loading ... </div> : <div>Product: {data}</div>
</div>
)
}
export async function getStaticProps({ params }){
return {
props: {
isloading: true
},
revalidate: 60,
};
}

Related

NextJS dynamically rendered components via SSR losing reactivity

I'm trying to use a Contentful references field to generate SSR landing pages which are populated with dynamic React components mapped to each content type.
The references field is basically an array of other content types that the user can add / edit / remove / reorder as they see fit:
The Contentful API is called in getServerSideProps.
export async function getServerSideProps(context) {
const config = require('../../config');
const contentful = require('contentful');
const client = contentful.createClient({
space: config.contentful.spaceId,
accessToken: config.contentful.deliveryAccessToken,
environment: config.contentful.environment,
});
const content = await client.getEntries({
content_type: 'landingPage',
'fields.slug': context.query.slug,
include: 2,
});
return { props: { landingPage: content.items[0]}};
};
The components are then rendered dynamically like so:
const LandingPage = (props) => {
return (
<MainLayout>
<div>{renderComponents(props.landingPage.fields.body)}</div>
</MainLayout>
);
};
renderComponents: (componentMap is just an object mapping item.sys.contentType.sys.id strings to React components)
const renderComponents = (data) => {
return data
.filter((item) => {
return item.sys.contentType.sys.id in componentMap;
})
.map((item, index) => {
const Component = componentMap[item.sys.contentType.sys.id];
const props = item.fields;
return (
<Component {...props} />
);
})
};
This all seems to works fine in both dev and production builds, however I've noticed that if I create another non-SSR page that uses these same components, all interactivity is lost from the SSR pages only.
This happens for all components on the dynamic page, even the ones that were not generated by the renderComponents function (for example, the navigation, which is standard across the entire site, and is part of MainLayout).
Deleting the non-dynamic pages immediately causes the interactivity return.
There are no error messages in either the browser console or terminal, which is making it difficult to debug exactly what is going wrong here.
Any advice appreciated,
Thanks
next.config.js is unchaged from default:
module.exports = {
useFileSystemPublicRoutes: true,
};

At what stage getStaticProps() fetch the data from my API?

I'm trying to understand when exactly does getStaticProps fetch the data.
is it on the build process or every time the website is been render to someone (every time someone visits my app)
if it's the first one, doesn't it make my app "static" meaning it won't update the data when the data on my DB is changed?
Thanks!!
I am going to sleep So I just copied below answer from NextJS Docs
Next.js allows you to create or update static pages after you’ve built your site. Incremental Static Regeneration (ISR) enables you to use static-generation on a per-page basis, without needing to rebuild the entire site. With ISR, you can retain the benefits of static while scaling to millions of pages.
To use ISR add the revalidate prop to getStaticProps:
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
}
}
export async function getStaticPaths() {
const res = await fetch('https://.../posts')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// We'll pre-render only these paths at build time.
// { fallback: blocking } will server-render pages
// on-demand if the path doesn't exist.
return { paths, fallback: 'blocking' }
}

NextJS SSR with context on refresh

I am trying to create a page with that goes like this - Each node can have X number of pods and when the user clicks on a particular node, he will be navigated to a page displaying all the pods belonging to this node.
I have managed to do so using dynamic routing from NextJS.
<Link
href={{
pathname: "/pods/[id]",
query: { nodeID: row.ID },
}}
as={`/pods/${row.ID}`}
>
<StyledButton>View Pods</StyledButton>
</Link>
const Pods = ({ pods }) => {
return (
<>
<PodTable pods={pods}></PodTable>;
</>
);
};
export async function getServerSideProps(context) {
const nodeID = context.query.nodeID;
// Fetch data from external API
const podRes = await fetch(`http://localhost:5000/v1/pods/filter/${nodeID}`);
const pods = await podRes.json();
console.log(`Pods Res: ${podRes}`);
// Pass data to the page via props
return { props: { pods } };
}
export default Pods;
While this generally works, the issue arise when the user navigates directly to the /pod/[id] page or refreshes the page without clicking the link button.
How should I fix my code to address this?

getServerSideProps does not render dynamic pages in production, and show 404 error

I'm working with next.js, in development mode everything works fine, but in production mode I have a problem when rendering the pages dynamically.
I have the following path inside the pages folder
pages/user/[id], and this component is where I call the getServerSideProps function.
import headers from '../../headers';
export async function getServerSideProps(context) {
const URL = 'https://somewhere...';
let { id } = context.params;
const apiResponse = await fetch(
`${URL}/${id}/detail`,
{
headers: headers,
}
);
if (apiResponse.ok) {
const data = await apiResponse.json();
return {
props: data, // will be passed to the page component as props
};
} else {
return { props: {} };
}
}
My problem is the following, I need to send in headers the authentication token that I only get when I login and I get the 2FA code, so in build time, that info does not exist and I get a 401 error no authorizate when execute npm run build and when I access to /user/34 for example I get a 404 error.
I have checked these questions at stackoverflow:
NextJs: Static export with dynamic routes
https://stackoverflow.com/questions/61724368/what-is-the-difference-between-next-export-and-next-build-in-next-js#:~:text=After%20building%2C%20next%20start%20starts,can%20serve%20with%20any%20host.&text=js%20will%20hydrate%20your%20application,to%20give%20it%20full%20interactivity.
next.js getStaticPaths list every path or only those in the immediate vicinity?
I have some parts in my app that are statics and works fine, but the problem is with the dynamic paths, as next.js is not creating those paths.
EDIT: I'll include a image with other problem, if after the fetch in the if I just say :
if(apiResponse){ //without 'ok'
}
I'll recieve this errror:
return {
props: data, // will be passed to the page component as props
}
props should be object
return {
props: {data} // or props: {data:data}
}

Pass data to getServerSideProps from previous page in NextJS

I am developing an e-commerce like website using NextJS.
I will fetch & display list of products in /products page. On clicking any product, I'll navigate to /details/[productId], and I'll fetch those product details as follows.
// In /details/[productId].js file
export async function getServerSideProps({params}) {
const res = await fetch(`https:my-api-url/api/products/${params.productId}`)
const product = await res.json()
return {
props: {
product
}
}
}
Problem
Everything looks good till this step. But I thought to reduce number of database read count, hence instead of fetching product detail again in detail page, I planned to use the data fetched in the previous page (/products) which will have the information about the product. Hence I need a way to pass those product object into next screen /details/[productId]'s getServerSideProps (to achieve SSR for SEO purposes).
Workaround
One solution I currently have is to stringify the product json and pass it via query parameter and get it back in getServerSideProps({params, query}). But it just spams my url in the browser which isn't look good at all.
Expectation
Is there any other way to pass the data into getServerSideProps function so that it will utilize the data to generate the whole page in server itself. Please guide me to overcome this issue. Any help would be appreciated.
Thanks in advance.. (:
You can bring in a custom server as express that provides locals property available through the lifetime of your application or request.
const next = require('next');
const express = require('express');
const app = next({ dev: process.env.NODE_ENV !== 'production' });
const handle = routes.getRequestHandler(app);
const env = process.env.NODE_ENV || 'dev';
app.prepare().then(() => {
const server = express();
server.get('/products', async (req, reply) => {
const products = await //... fetch product with details
req.app.locals.products = products;
return app.render(req, reply, '/path/to/products/page', req.query);
});
server.get('/details/:productId', async (req, reply) => {
const {productId} = req.params;
const {products} = req.app.locals;
// find product using productId and make available in req.locals
req.locals.product = // product;
return app.render(req, reply, '/path/to/product/detail/page', req.query)
});
server.get('*', (req, reply) => {
return handle(req, reply)
});
server.listen(3000);
});
Pay caution to how large your product list grow to avoid running your application out of memory.
You could also return a cookie containing the list of products on the request for products (See limits for HTTP cookies). Then read that on the product detail page.
When I enter URL http://localhost:3000/blog/wfe436
//getting the meta tags dynamically
export const getServerSideProps = async ({ params }) => {
// Get external data from the file system, API, DB, etc.
console.log(params) // here is the data of the url { blogname: 'wfe436' }
const posts = Data
// The value of the `props` key will be
// passed to the `Home` component
return {
props: { posts }
}
}

Resources