I read Next.js documentation many times, but I still have no idea what's the difference between getStaticProps using fallback:true and getServerSideProps.
As far as I understand :
getStaticProps
getStaticProps is rendered at the build time and serves any requests as static HTML files. It is using with pages that are not updated often, for example, an About us page.
export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }]
}
}
But if we put fallback:true at the return of the function, and there is a request to the page that is not generated at the build time, Next.js will generate the page as a static page then other requests on this page will be served as a static.
export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
fallback: true,
}
}
So, getStaticProps's concept is working great for me. I think it can work for most scenarios. But if getStaticProps is working great, then why do we need getServerSideProps?
I understand that Next.js will pre-render each request if we use getServerSideProps. But why do we need it, when we can use getStaticProps to get the newest data, which I think it is better for TTPB?
Can you guys please explain it to me or guide me to something that can help me totally understand this?
Consider a web crawler that does not run Javascript, when it visits a page that was not built at build time, and its fallback is set to be true, Next.js will serve a fallback version of the page that you defined (Fallback pages). The crawler may see something like <div>Loading</div>.
Meanwhile, if the crawler visits a page with getServerSideProps, Next.js will always respond with a page with all the data ready, so the crawler will always get the completed version of the page.
From a user standpoint, the difference is not significant. In fact, the page with getStaticProps and fallback: true may even result in a better perceived performance.
Also, as more and more crawlers execute JavaScript before indexing JavaScript, I would expect there will be fewer reasons to use getServerSideProps in the future.
fallback:true is set inside getStaticPaths which is used with getStaticProps to pregenerate the dynamic pages.
getStaticPath fethces the database to determine how many pages have to be pregenerated and what would be the dynamic part which is usually "slug" for blogs and "id" for products. Imagine, your page has 1000s of products, and pre-generating each page will take long time. If user just manually types the url "yourDomain/products/100". If that page is not ready, you will be returning a Fallback component.
However getServerSideProps get executed upon request hits the route. with getStaticPaths and getStaticProps(if you are revalidating you have access to request), you have no access to request object. for example you might need to extract the cookie and make an api call with this cookie.
Otherwise imagine you have dynamic page "users/[id]", and you are showing users secret data here upon authentication. If you pregenerate this pages, then all other users can see each page.
Related
I'm using a Headless CMS based in another country that is not near my own, and each request to this CMS takes at least 1.5s to return a payload (which can be a ridiculously long time to wait when you're making multiple requests).
Now, I can use getStaticProps for each page requiring CMS content. But, since I have global components that need information from the CMS too (such as the navigation and footer), this destroys Next's ability to render each page statically, and it forces me to use getInitialProps in _app.js.
Now, before you say:
Just create a request to the CMS on every page using getStaticProps.
That will remove the ability for my website to be a proper single-page application and cause a re-render for elements in the layout that don't need to be re-mounted while navigating. This ability to navigate without re-mounting is vital since the navigation has functionality that relies on it.
Here is an example of what my _App.getInitialProps looks like:
const appProps = await App.getInitialProps(context);
try{
// Retrieve data from CMS.
// Yes. I do have to create two requests to the CMS to retrieve a full payload,
// as that is how their API is setup, unfortunately.
const dataOne = await fetch(...);
const dataTwo = await fetch(...);
// Yes, I'm aware this isn't actually how it would work.
return { ...dataOne, ...dataTwo, ...appProps };
} catch(error){
console.log("Error in _App component:", error);
}
return appProps;
The ideal workaround for this would be to use getStaticProps in each component that requires it - but this isn't a feature that Next offers, unfortunately.
I am working on a website that uses Nextjs and React. We are using a mix of static generation as well as SSR. One of the components we're using is shared by every page on the site. It is a header. The issue is that his header requires some very large queries in order to get all the information it needs to properly render. Ideally, we want to "statically render" this component so that it can be used by all pages at build time, even for pages that are not statically rendered. As it stands, rendering this header every time a user visits our website significantly increases load times as well as puts an unnecessary load on our DB.
Is there any way to go about doing something like "statically rendering" a single component that's shared by every page? Part of the code for the header looks something like
const { loading, error, data } = useQuery(QUERY);
const { loading: productLoading, data: productData } = useQuery(QUERY_PRODUCT);
const { data: otherData } = useQuery(OTHER_DATA);
const { data: otherOtherData } = useQuery(OTHER_OTHER_DATA);
Ideally we don't want to run these queries every time a user visits the website.
If you are going to use the same data for every page, you may consider using a redux-store or storing them in a context provider.
You may use the codes sample similar to https://stackoverflow.com/a/64924281/2822041 where you create a context and provider which is accessible in all your pages.
The other method, ideally, would be to "cache" using Redis so that you do not need to query the data over and over again.
Do you know if it's possible to re-execute Gatsby page queries (normal queries) manually?
Note, This should happen in dev mode while gatsby develop runs.
Background info: I'm trying to set up a draft environment with Gatsby and a Headless CMS (Craft CMS in my case). I want gatsby develop to run on, say, heroku. The CMS requests a Gatsby page, passing a specific draft-token as an URL param, and then the page queries should be re-executed, using the token to re-fetch the draft content from the CMS rather than the published content.
I'm hooking into the token-request via a middleware defined in gatsby-config.js. This is all based on https://gist.github.com/monachilada/af7e92a86e0d27ba47a8597ac4e4b105
I tried
createSchemaCustomization({ refresh: true }).then(() => {
sourceNodes()
})
but this completely re-creates all pages. I really only want the page queries to be extracted/executed.
Probably you are looking for this. Basically, you need to set an environment variable (ENABLE_GATSBY_REFRESH_ENDPOINT) which opens and exposes a /__refresh webhook that is able to receive POST requests to refresh the sourced content. This exposed webhook can be triggered whenever remote data changes, which means you can update your data without re-launching the development server.
You can also trigger it manually using: curl -X POST http://localhost:8000/__refresh
If you need a detailed explanation of how to set .env variables in Gatsby just tell me and I will provide a detailed explanation. But you just need to create a .env file with your variables (ENABLE_GATSBY_REFRESH_ENDPOINT=true) and place this snippet in your gatsby-config.js:
require("dotenv").config({
path: `.env.${activeEnv}`,
})
Of course, it will only work under the development environment but in this case, it fits your requirements.
Rebuild for all is needed f.e. when you have indexing pages.
It looks like you need some logic to conditionally call createPage (with all data refetched) or even conditionally fetch data for selected pages only.
If amount (of pages) is relatively not so big I would fetch for all data to get page update times. Then in loop conditionally (time within a few minutes - no needs to pass parameter) call createPage.
If develop doesn't call 'createPage' on /__refresh ... dive deeper into gatsby code and find logic and way to modify redux touched nodes.
... or search for other optimization techniques you can use for this scenario (queried data cached into json files?).
I already searched for a good while on the Internet and even checked all suggested questions here, but I found nothing.
Basically, I'm using vue-router to load views when the user clicks on them (without prefetching, just lazy-loading), using a function that imports the Vue view/component. To better visualize, I made a barebone example of what I'm doing:
const router = new VueRouter({
routes: [
...
{
path: "/a_page",
component: function() {
return import("./views/A_Page.vue");
}
}
]
});
I'm using Express in the backend to protect certain routes, because protecting it in the Frontend is wasted effort, since the user could bypass the 'protection' easily, if he wants to. Also all views have their own splitted .js file (using "webpackChunkName") and Express needs a Bearer Authentication Token header for every API call OR .js file requested. This works great with Axios (responsible for fetching API data) where you can manually define a header, but vue-router hasn't this option, and since it doesn't send the Authorization header, it doesn't authenticate, Express blocks the file with a 403 and vue-router fails to import the file.
Is there any way to send the Authorization header with the import (which is basically just a GET request)?
Thanks in advance.
If someone thinks I'm approaching the problem in a wrong way, feel free to comment and suggest another way.
EDIT: The suggested duplicate question was given too little attention and the only solution given (which is basically split in 2) doesn't work with the current webpack anymore; onload(event) and onerror(event) get undefined.
You could use a router guard instead of protecting with basic auth.
I use this method, along with lazy loaded routes. If the auth fails you can redirect the user to a login page.
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
If (auth) { // get value of cookie etc for a jwt token or similar auth method
next() // proceed
}
else {
next(false) // cancel navigation
// or
next(“/login-url”) // redirect you login url
}
})
Additionally, you could use an axios method to auth on each route change.
If you want to send up the Authorization header (which doesn't seem to be an easy task, given that no one knows how to go about it...) I think you could override webpack's jsonp function that it uses to load the chunks in splitChunks...
Here's the docs for the webpack function that loads the chunks
You'll override your webpack config with your modified chunk loading function and then tie that into your vue.config.js like so...
// vue.config.js
module.exports = {
configureWebpack: require('./webpack.config.js')
}
All this being said, I would suggest protecting your frontend assets much earlier than when you need to be loading your split chunks and not requiring the Authorization header to serve your static assets.
Sometimes you can do this at the network layer (load balancer, etc) depending on your use-case. Other times using a server-based approach, like rendering your app w/ Nuxt, will be what you want.
If I'm understanding correctly (feel free to correct me), would you be able to do an auth call with axios prior to the actual routing, or perhaps upon the routing using a mounted call? Especially if there is no valid authentication you can then either redirect to a login page or re-route to an error page.
Feel free to let me know if I'm misunderstanding.
We have a site with most of the content managed by Wordpress, however when the user navigates to search pages (user searches for a product), it's handled by React JS.
It's all on the same domain, so the user never knows that they are interfacing with two different applications.
Google Analytics on the site, however, doesn't seem to perceive sessions correctly. It's logging entrances (landing pages) to the site as search pages with rather long URLs:
There are thousands of landing pages like this, and the site is new, so there's no way this is all traffic is coming in from external links
Referrer path for all of these sessions is "(not set)"
Internal IP addresses are filtered
The traffic is coming from various sources/mediums, suggesting that sessions are somehow breaking (screenshot below)
Currently, GA is set up with GTM. I tried using this to fire the GTM tag in React.
Also attempted making the GA tag within GTM fire on browser history changes rather than page views (history changes fire when in React, normal page views in Wordpress). But the issue still persists with these modifications.
Note that these sessions are not specific to any one browser:
The issue you're experiencing comes from the fact upon search, you are switching your entry point and doing a hard refresh of your page to the React app. Even though the domain doesn't seem to change, it's still considered by the browser as a fresh page load and thus showing like so in your analytics, as shown by this request:
You haven't really told if you were using react-router in your app (I'm assuming you are given the different paths), a way to get around the problem would be to use react-ga instead of the default GA script and leverage the onUpdate callback of react-router.
First import and initialize the module with your GA id:
import ReactGA from 'react-ga'
ReactGA.initialize('UA-000000-01')
Then in your routes configuration, add the onUpdate property on the <Router> and create a method that will call the google analytics with only the pathname so you won't end up with all the query parameters that are quite obnoxious in the dashboard.
const onUpdate = () => {
ReactGA.set({ page: window.location.pathname })
ReactGA.pageview(window.location.pathname)
}
<Router onUpdate={onUpdate}>
...
</Router>
Still, if you want to keep track of the user search, I would recommend using events instead, and doing something like the following upon search:
ReactGA.event({
category: 'User',
action: 'Search',
value: 'your formatted search value'
})
It will also give you the ability to format the value of the search any way you want, which would be more readable for you than query parameters.