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

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

Related

How to manage global states with React Query

i have a project that's using Nextjs and Supabase. I was using context API and now i'm trying to replace it for React Query, but i'm having a hard time doing it. First of all, can i replace context completely by React Query?
I created this hook to get the current user
export const getUser = async (): Promise<Profile> => {
const onFetch = await supabase.auth.getUser();
const userId = onFetch.data.user?.id;
let { data, error } = await supabase
.from("profiles")
.select()
.eq("id", userId)
.single();
return data;
};
export const useUser = () => {
return useQuery(["user"], () => getUser());
};
I'm not sure how to trigger it. When i was using context i was getting the user as this. If data, then it would redirect to the HomePage
let { data, error, status } = await supabase
.from("profiles")
.select()
.eq("id", id)
.single();
if (data) {
setUser(data);
return true;
}
Since i was getting the user before redirecting to any page, when i navigated to profile page, the user was already defined. How can i get the user before anything and keep this state? I suppose that once the user is already defined, at the profile component i can call useUser and just use it's data. But it's giving me undefined when i navigate to profile, i suppose that it's fetching again.
const { data, isLoading } = useUser();
But it's giving me undefined when i navigate to profile, i suppose that it's fetching again.
Once data is fetched when you call useUser, it will not be removed anymore (unless it can be garbage collected after it has been unused for some time). So if you do a client side navigation (that is not a full page reload) to another route, and you call useUser there again, you should get data back immediately, potentially with a background refetch, depending on your staleTime setting).
If you're still getting undefined, one likely error is that you are creating your QueryClient inside your app and it thus gets re-created, throwing the previous cache away. You're not showing how you do that so it's hard to say. Maybe have a look at these FAQs: https://tkdodo.eu/blog/react-query-fa-qs#2-the-queryclient-is-not-stable

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

My react component never displays the information from the database

I have a small web app displays game information.
I am using React hooks so that the component is modern.
When this component loads, I want it to connect to the api via axios, and get the description of the game.
But when it loads, the value inside the <GameVault /> is always null.
When I look in the database, it is not null. If I hit the api directly, it does return the game description.
My console.log is hit twice for some reason. The first time it's null, the second time it has the needed value.
I am also not getting any errors, so I don't know why this isn't working.
Here is the code:
const Vault = ({ game }) => {
const [gameText, setGameText] = useState(null);
async function fetchGameText() {
const response = await axios.get(`/api/gamermag/${game.id}/gameDescriptionText`);
setGameText(response.data);
}
useEffect(() => {
fetchGameText();
}, []);
console.log("gameText: ", gameText);
const gamerValue = useMemo(() => {
return gameText ? gameText : "";
}, [gameText]);
return (
<GameVault value={gamerValue} />
)
}
export default Vault;
Is there a way to get this to work?
Thanks!
You need to wait for the data to load from the server. While the data is being fetched, gameText will be null and when it's done fetching, it stores the response. That is why your console.log hit twice. The first time is the component's first render, and the second time is when the gameText changes its state which caused a rerender.
You need to add logic to wait for the data.
if(!gameText){
return <div>loading...</div>
}

React multiple http calls when navigating the application using the URL

I have a component which has filtering, searching and pagination capabilities. What I'm trying is to create a queryString and attach to the URL such that I can later copy and paste it in another browser so that I can reuse the filters.
To extract the query params from the URL I'm using the useLocation hook
const useQuery = () => new URLSearchParams(useLocation().search);
const pageNo = useQuery().get('page') ?? 1;
I'm using the useEffect hook to track for changes of the page query parameter value, and dispatch an action which will update the pageNo in the state object of my reducer.
React.useEffect(() => {
dispatch({
type: actionDescriptor.CHANGE_PAGE,
payload: pageNo
});
}, [pageNo]);
I have another useEffect hook which handles the fetch of the data, and gets triggered when the pageNo changes. I'm using the useNavigate to create and navigate to the new location if the http call was successful
const nav = useNavigate();
React.useEffect(() => {
(async function get() {
const response = // make http call and get response
if (response.status === 200) {
dispatch({
type: actionDescriptor.FETCH_SUCCESS,
payload: {
data: response.data['value'],
}
});
nav (`/data?page=${state.pageNo}`);
}
/// handle error
}
})();
}, [state.pageNo, state.pageSize, state.filter]);
When I'm navigating using the UI, selecting a different page for example, all works well, there is a single http call, the browser url is updated as expected (e.g. localhost/mydata?page=2). If however I'm copying the url and paste it in another window, it makes two http calls, and renders the dom twice. What might be the cause for this?
my guess is due to the parameters you are listening on state.pageNo, state.pageSize, state.filter. I'm assuming all of these are null/empty at the beginning of your app. Once you copied and pasted the url, two of these parameters will change which will cause the useEffect to be called twice.
put in a console.log in the useEffect to confirm that. Once that's confirmed, I would re-examine the list of parameters to see if you need to listen to all of them.
I would take a look at the pageNo. It looks like it might be changing from default value to 2 since you have 2 useEffects probably firing for the same render.

Preloading getServerSideProps data with Next.js?

I've got a simple React component:
const Page = ({ data }) => {
return (
<header>
{data.length !== 0 ?
<>
{data((d) =>
// render data
)}
</>
:
<>Loading...</>
}
</header>
)
}
I'm getting the data using Next.js recommended getServerSideProps:
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`someurl`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
Now, for the love of God, I can't figure out why <>Loading...</> is never rendering. The page is blank and then everything pops up. Why does it happen and how do I fix that? of course data.length IS 0 before it's fetched...
Note I'm using dynamic routing and do not want to go with getStaticProps.
getServerSideProps always runs on server side also for client side navigation.
When you return data from getServerSideProps (if the fetch method is executed without errors) it will have always return a value.
In your example <Loading /> will be visible only if data returned from fetch has 0 length and will never be visible during fetch.
Here the docs https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering
It's obvious that user should not wait a few seconds in which nothing happens (because getServerSideProps is not finished loading) when he clicks a link. He should see some action is happening, for example:
Loading spinner
Data template (boxes for images, text and so on), youtube example.
But for now it's not possible with getServerSideProps, because page is rendered only after getServerSideProps request is complete.
There is exist future request on next.js about this, so i hope it will be implemented.
you need to use isFallback method provided by there next/router. have a look on this code try to look for isfallback https://github.com/vercel/next-site/blob/master/pages/docs/%5B%5B...slug%5D%5D.js.
Edit:
`export async function getServerSideProps() {
Fetch data from external API
const res = await fetch(someurl)
const data = await res.json()
Pass data to the page via props
return {
props: res ? {
data,
id,
url,
} : {}
};
}
`
and in your component
const router = useRouter();
const { isFallback } = router
if (isFallback) {
return <Loading />
}
else {
return (
// render data
)
}

Resources