React async data fetch does not fetch data on static build - reactjs

I'm building a static site using Sanity.io and Gatsby.js.
The data is hosted through Sanity and I'm fetching it via GROQ.
Host and build is on Netlify.
The problem is that when I build I only get a rendered result of Loading. It does work in development. (which, I'm sure is because of hotloading)
How would I build a render a (async) data fetch for a build? I don't want to have this in a componentDidMount() or useEffect() Because from what I understand then the query would be client side. that would result in an API request for every page load, right? I just want one on build instead.
Can I pause the build/render just as long as it has no data yet?
Below is my simplified code
import React from "react"
import useSWR from "swr"
import client from "../../../../../sanityClient"
import BlockCarousel from "../blockCarousel/blockCarousel"
const BlockCarouselBlog = ({ block }) => {
const skip = 0
const limit = block.itemAmount ? block.itemAmount : 10
const params = { skip, limit: skip + limit - 1 }
let {
data,
error,
} = useSWR(
'*[_type == "blog"] | order(_createdAt asc) [$skip..$limit]',
query => client.fetch(query, params)
)
if (error) console.log(error)
if (!data) return <div>Loading</div>
block.blocks = data
return <BlockCarousel block={block} />
}
export default BlockCarouselBlog

If you want to use GROQ to fetch data this way, and you don't want to do it client side, you probably want to either create a custom source plugin that creates GraphQL nodes using Gatsby's APIs, or define a pre-build function that will fetch all of your data and make it available in static JSON files or similar, then import those into your components or fetch them client side.
Using GROQ with Gatsby without using GraphQL for statically rendered components is going to be a bunch of additional work, and you lose a lot of the value of Gatsby over something like Next.js.

Related

Module federation - SSR data fetching on remote module

The problem: I am trying to set up a simple module-federation enabled React application that uses server-side rendering.
There is a host application hostServer. It consumes a remote component called SharedComponent, which is served by another application called remoteServer.
I want the SharedComponent to do some data fetching and have the results be part of the initial server render.
The code: I am starting with Zack Jackson's SSR example here:
https://github.com/module-federation/module-federation-examples/tree/master/server-side-render-only
It does all I need minus the data fetching.
What I've tried: Modifying the SharedComponent.js along the lines of the following - which I know won't work, but I couldn't think of anything else.
import React from 'react';
import fetch from 'node-fetch';
const SharedComponent = () => {
const [data, setData] = React.useState([])
async function getData(){
const data = await fetch('https://swapi.dev/api/planets')
const json = await data.json();
setData(json)
}
React.useEffect(() => {
getData()
}, [])
return (
<>
<div>Shared Component2222</div>
<p>Data; {JSON.stringify(data)}</p>
</>
);
}
export default SharedComponent;
Result: As expected, the data fetching happens client side rather than server side.
How can I get SharedComponent to do server-side data fetching?

How to fetch data from Server Side using Next JS

Normally React JS Using useEffect for Client Side Data Fetching but I want to fetch Data in server side in Next Js that time the code also reusable any one can clear my doubt
function MyPageComponent({ data }) {
// You page component that will be displayed on frontend
// Here you can use useEffects that will be executed on the frontend as the page loads
}
// This gets called on every request on the BackendOnly!
export async function getServerSideProps() {
// Do your server logic here, like fetching some api to get an initial state
const res = await fetch(`site url`)
const data = await res.json()
// Pass data to your page component via props
// This will be accessible on the frontend
return { props: { data } }
}
export default MyPageComponent

How to show loading state only if data is not received yet. Next.js ssr

I have multiple getServerSideProps in my project and I have a header which displays pages and I have to wait for a page to be opened once I click upon it since I need data to be fetched. Once they are fetched the page will be open.
One approach I used to show user a loading state is to use routeChangeStart BUT I stumbled upon one problem and so I would like not to use this case.
If I go on a page and the data is fetching I want to show user a spinner or some indicator and once the data is fetched I want to stop the indicator/spinner.
As you probably figured out, getServerSideProps runs on the server and is blocking. The fetch request needs to complete before the HTML is sent to the user (i.e., the page is changed). So if you want to show a loading indicator, you need to move that fetch request to the client.
For instance, if you probably have a page with this basic structure:
export default function Page({ data }) {
return <div>{data.name}</div>
}
export async function getServerSideProps() {
const response = await fetch('https://example.com/api')
const data = await response.json()
return {
props: { data },
}
}
const fetcher = url => fetch(url).then(res => res.json());
export default function Page() {
const { data } = useSWR('https://example.com/api', fetcher)
if (!data) return <LoadingSpinner />
return <div>{data.name}</div>
}
Or if you don't need SWR and can use a simple fetch request:
export default function Page() {
const [data, setData] = useState()
useEffect(() => {
fetch('https://example.com/api')
.then(async(response) => {
const json = await response.json()
setData(json)
})
})
if (!data) return <LoadingSpinner />
return <div>{data.name}</div>
}
P.S. If the initial fetch request in getServerSideProps used sensitive information (e.g., API secret credentials), then go ahead and setup a Next.js API route to handle the sensitive part and then fetch the new route.
I just used routeChangeStart.
I didn't want to use it since router.push('/map') didn't work in pages/index.tsx file but I solved this issue by creating a new component putting router.push in useeffect and rendering a loader.
routeChangeStart was in _app.js and because of this in index.js router.push() didn't work - I tested it
routeChangeStart - how it works?
When we click on a page the data is being fetched on the server and the page will only be displayed to us once the data is fetched. So we can make the next thing, we can just intercept the route change.
When we click on a link(we wait for data to fetch) we set loading state in routeChangeStart to true and if we moved to another page(it means we fetched the data) we invoke routeChangeComplete which runs once we moved to the route we wanted to, and here we set loading state to false. And after this I just pass the loading state using React Context

Dynamic route segment in nextJS static js build

is it possible to use dynamic routing path with nextJS, in a static build context?
Assuming my build is a set of html, js, css assets which could be hosted on any web server (Apache, Nginx, S3, Netlify etc...).
For instance I've got a path defined /pages/[article].js/read and I'd like to be able to use the structure: /page/articleA/read where articleA is a dynamic variable.
If that's feasible, how to achieve this?
Yes, it is possible to use dynamic routes when using static-site-generation with nextjs. You have to use data-fetching method getStaticProps to fetch the data needed based on the dynamic route param. Additionally you have to use another function getStaticPaths to generate a list of paths for which nextjs will build static pages at build time. For example,
Suppose for page /pages/articles/[articleId].js, Here is what pseudo code might look like for you.
// you have to generate and return a list of paths
export const getStaticPaths = async () => {
const articles = await /*Fetch the articles from backend or make a db query*/
const paths = articles.map(article => ({ params: { articleId: article.id }}));
return {
paths,
fallback: false
}
}
export const getStaticProps = async (ctx) => {
const articleId = ctx.params.articleId;
// fetch the data using the article id and return as props
return {
props: /* fetched data */
}
}
// create the page component and export it as the default export
You can read more about it in the docs. Keep in mind because the fallback is set to false nextjs will show a 404 page for any path that is not returned from the function getStaticPaths, You can read about fallback here.

NextJS: Get data from local storage in getInitialProps method

I try to migrate my project to Next.js framework for having SSR (Server side rendering) feature. Here is my simple page:
class Example extends React.Component {
static getInitialProps() {
// api getting data. Need authentication data under local storage
}
render() {
return <div>{this.props.data}</div>;
}
}
The problem I met is: I want my data is from getInitialProps first (that is the purpose of SSR). But when sending, I need a information about user for backend API. But at this function, rendering is happened on server so I cannot get data from local storage. How can you solve this problem.
Thanks
You can put the call to localStorage into a react component lifecycle method such as componentDidMount and then setState() with the result.
It's not great, but will work.
Note that this would obviously not use SSR.
This works for me:
const USER = 'user'
const URL = 'http://www.example.com/user'
// here you can also use isServer
if (req) {
const {data: user} = await Axios.get(URL)
localStorage.setItem(USER, JSON.stringify(user))
} else {
const user = await JSON.parse(localStorage.getItem(USER))
}

Resources