Show dynamic loader when requesting ssr props - next.js

The problem:
getServerSideProps is blocking the whole site on subsequent requests, even when only the props are requested and all other js is already loaded.
So I was wondering if it is possible to add a loading component to each page (something like the dynamic layout: https://github.com/vercel/next.js/tree/canary/examples/layout-component)
and show it instantly while waiting till the props are loaded.
I know about the Router from nextjs and the events but how would I show the loader when I got only the url on routeChangeStart?

I use getInitialProps now. Thought it was deprecated, but it's not.
I can now do something like this:
const MyPage = (props) => {
const [data, setData] = useState(null)
useEffect(() => {
if (props.data) {
setData(props.data)
} else {
// fetch client side and setData
}
});
return (
<>
{data === null && <LoadingMyPage/>}
{data && <TheActualContent/>}
</>
);
}
MyPage.getInitialProps = async () => {
if (window) return {data: undefined};
// fetch server side
return {data: ...}
}

Related

React Query - useQuery callback dependent on route parameter? [duplicate]

When page is refreshed query is lost, disappears from react-query-devtools.
Before Next.js, I was using a react and react-router where I would pull a parameter from the router like this:
const { id } = useParams();
It worked then. With the help of the, Next.js Routing documentation
I have replaced useParams with:
import { usePZDetailData } from "../../hooks/usePZData";
import { useRouter } from "next/router";
const PZDetail = () => {
const router = useRouter();
const { id } = router.query;
const { } = usePZDetailData(id);
return <></>;
};
export default PZDetail;
Does not work on refresh. I found a similar topic, but manually using 'refetch' from react-query in useEffects doesn't seem like a good solution. How to do it then?
Edit
Referring to the comment, I am enclosing the rest of the code, the react-query hook. Together with the one already placed above, it forms a whole.
const fetchPZDetailData = (id) => {
return axiosInstance.get(`documents/pzs/${id}`);
};
export const usePZDetailData = (id) => {
return useQuery(["pzs", id], () => fetchPZDetailData(id), {});
};
Edit 2
I attach PZList page code with <Link> implementation
import Link from "next/link";
import React from "react";
import TableModel from "../../components/TableModel";
import { usePZSData } from "../../hooks/usePZData";
import { createColumnHelper } from "#tanstack/react-table";
type PZProps = {
id: number;
title: string;
entry_into_storage_date: string;
};
const index = () => {
const { data: PZS, isLoading } = usePZSData();
const columnHelper = createColumnHelper<PZProps>();
const columns = [
columnHelper.accessor("title", {
cell: (info) => (
<span>
<Link
href={`/pzs/${info.row.original.id}`}
>{`Dokument ${info.row.original.id}`}</Link>
</span>
),
header: "Tytuł",
}),
columnHelper.accessor("entry_into_storage_date", {
header: "Data wprowadzenia na stan ",
}),
];
return (
<div>
{isLoading ? (
"loading "
) : (
<TableModel data={PZS?.data} columns={columns} />
)}
</div>
);
};
export default index;
What you're experiencing is due to the Next.js' Automatic Static Optimization.
If getServerSideProps or getInitialProps is present in a page, Next.js
will switch to render the page on-demand, per-request (meaning
Server-Side Rendering).
If the above is not the case, Next.js will statically optimize your
page automatically by prerendering the page to static HTML.
During prerendering, the router's query object will be empty since we
do not have query information to provide during this phase. After
hydration, Next.js will trigger an update to your application to
provide the route parameters in the query object.
Since your page doesn't have getServerSideProps or getInitialProps, Next.js statically optimizes it automatically by prerendering it to static HTML. During this process the query string is an empty object, meaning in the first render router.query.id will be undefined. The query string value is only updated after hydration, triggering another render.
In your case, you can work around this by disabling the query if id is undefined. You can do so by passing the enabled option to the useQuery call.
export const usePZDetailData = (id) => {
return useQuery(["pzs", id], () => fetchPZDetailData(id), {
enabled: id
});
};
This will prevent making the request to the API if id is not defined during first render, and will make the request once its value is known after hydration.

nextjs HOC to verify routes

Im trying to make some validations before render all the content. And i am having 2 issues if o try this in different ways.
If i just do the validation like this:
const VerifiedRoute: React.FC = ({ children }: any) => {
const user = useAuth();
if (process.env.otp || !user.hasOTP) {
Router.replace(routes.auth);
return null;
}
return <>
{children}
</>;
};
I get "Error: No router instance found."
I tried with a useEffect like this:
const VerifiedRoute: React.FC = ({ children }: any) => {
const user = useAuth();
useEffect(() => {
if (process.env.otp || !user.hasOTP) {
Router.replace(routes.auth);
return null;
}
}, [user])
return <>
{children}
</>;
};
And the redirect works.
But as soon as i get redirected, the page open and i get this error:
"InvalidStateError: Failed to execute 'postMessage' on 'BroadcastChannel': Channel is closed".
I'll assume that at the first render, the BroadcastChannel is still closed, and it just opens after the useEffect run.
But the console shows this error
Any idea how can i fix this?

How do we get window.location.hash from a Next.js application?

If we stick window.location.hash in useEffect, it will always erroneously return '0'. Apparently it has to do with SSR.
I need to reliably be able to get the hash portion of the URL for my project. How should I best go about it?
Server side code needs to wait until the code is loaded in the browser to use browser APIs.
Vanilla js server-side compatible
const [isMounted, setMounted] = useState(false);
useEffect(() => {
if (isMounted) {
console.log('hash', window.location.hash);
} else {
setMounted(true);
}
}, [isMounted]);
if(!isMounted) return <>Loading...</>;
Using next/router
import { useRouter } from 'next/router';
const { asPath } = useRouter();
useEffect(()=>{
const hash = asPath.split('#')[1];
}, [ asPath ]);
FYI you're code shouldn't return a zero. The first culprit that comes to mind is when a shorthand condition is used without an else.
window && window.location.hash
this should have an else
(window && window.location.hash) || null
or
window && window.location.hash ? window.location.hash : null
Extending #Sean W's answer, if you want to get a specific hash value from a hash key, you can use URLSearchParams:
// example path: /#error=unauthorized_client&error_code=401error_description=Something+went+wrong
import { useRouter } from 'next/router';
const { asPath } = useRouter();
useEffect(() => {
const hash = (asPath as string).split("#")[1]; // error=unauthorized_client&error_code=401error_description=Something+went+wrong
const parsedHash = new URLSearchParams(hash);
const errorHash = parsedHash.get("error_description"); // Something went wrong
}, []); // `asPath` omitted from dependencies as ESLint states it won't change

How to use redux hooks in getInitialProps

I have been trying to persist my redux store through a reload. I was using useEffect to dispatch my actions at first but then when I tried to reload the page router became undefined and I got a 500 error. After that I tried using getInitialProps and use the ctx.query.id but I ran into another error saying that hooks can only be called inside of the body of a function component.
How do I make it so hooks work inside of getInitialProps and what is the best way of persisting my store data through a reload?
export default function CarPage() {
const dispatch = useDispatch()
const router = useRouter()
const car = useSelector((state) => state.cars.car)
/*
useEffect(() => {
if(!car && router) {
dispatch(getCar(router.query.id))
}
}, [])
*/
return (
<Container>
<h2>{car.model}</h2>
</Container>
)
}
CarPage.getInitialProps = async (ctx) => {
const dispatch = useDispatch()
dispatch(getCar(ctx.query.id))
}
To persist redux store through a page reload, we definitely need to use browser storage.
I suggest using https://github.com/rt2zz/redux-persist.
To use dispatch inside getInitialProps, please try with this code snippet instead of using useDispatch() hook.
CarPage.getInitialProps = async ({ store, query }) => {
store.dispatch(getCar(query.id));
return { initialState: store.getState() };
}

Not getting API response data in props using getStaticProps in next js

I am new to nextjs. I want to prerender a page on server and want to delay rendering till API call is resolved. to achieve this i am using getStaticProps as mentioned in nextjs official docs.
here is the position of the file in my code structure:-
i am exporting my getStaticProps from index.js
here is the code snippet :-
export const getStaticProps = async () => {
const res = await axios.get(`http://blogexample.com/blog/posts`);
const blogList = await res.data
return {
props: {
blogList
}
}
}
const Blog = (props) => {
const { blogList } = props;
useEffect(() => {
console.log('list',blogList)
},[blogList])
return(
....
);
}
export default Blog;
problem is that, in browser console....my console.log('list',blogList) statement prints undefined
what i am doing wrong
getStaticProps is executed during build time. Use getServerSideProps
instead.

Resources