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?
Related
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.
target: 'static'
ssr: false
I have configured my project with ssg.
If you look at fetch in the official nuxt documentation, it says that fetch is executed when a page is created and cached in the client.
However, when data is fetched from fetch and the corresponding url is accessed, an empty area appears and then the content is filled. This means calling from the client.
How can fetch be executed when page is created and all db data can be seen when url is accessed?
<template>
<div>
{{ testData }}
</div>
</template>
<script>
import { defineComponent, ref, useContext, useFetch } from '#nuxtjs/composition-api'
export default defineComponent({
name: 'Test',
fetchOnServer: false,
setup () {
const testData = ref({})
const { $axios } = useContext()
useFetch(async () => {
testData.value = await $axios.get('https://jsonplaceholder.typicode.com/users/1')
})
return {
testData
}
}
})
</script>
<style>
</style>
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,
};
}
I want to display pages with different states based on different query params in next.js , so that when a user uses a link with query param , It should not show the default page , but based on the state.
For simplicity
Coinsider the home page as localhost:3000
This renders some default text 'Hello user'
If user enters localhost:3000/?name=JohnDoe
This should render Hello JohnDoe.
Things I have tried .
I am able to add the query param using event handler's via next router .
router.push('/?name=JohnDoe)
But I dont know of a solution to set the state when the user enters the page directly using the a link with query params
You can access the query in getServerSideProps and then redirect or do whatever you want with that query :
const getServerSideProps = (ctx) => {
const {params} = ctx;
if(params){
return {
redirect: {
permanent: false,
destination: "/login",
}
}
}
To read query params, you should use router.query.
Here is an example that would render 'Hello user' on localhost:3000, and 'Hello JohnDoe' on localhost:3000?name=JohnDoe
import { useRouter } from 'next/router';
const Page = () => {
const router = useRouter();
const userName = router.query.name ?? 'user';
return <div>Hello {userName}</div>;
};
export default Page;
Assume I have the following page in my next.js react application:
// Filename: [mypath].jsx
export default function MyPage(props) {
return (
<>
<Link href="/siteX">
<a>Go to siteX</a>
</Link>
<Link href="/siteY">
<a>Go to siteY</a>
</Link>
<div>{props.data.text}</div>
</>
);
}
export async function getServerSideProps(context) {
const mypath = context.params.mypath;
const res = await fetch(`https://external-api/${mypath}`)
const data = await res.json();
return { props: { data } };
}
When I access http://localhost:3000/siteX/ on server side the string siteX from the url is used for a call to an external (!) api on a different system, e.g. https://external-api/siteX. This works fine so far, but I see the following performance issue:
In the browser, when I click on a <Link>, two requests are happening: One to my own server to update getServerSideProps with the new path and a second one from my server to https://external-api/... to fetch the new data.
Do you see a way to optimize this? What I want is:
when clicking on <Link> there is only one request directly to https://external-api/... happening and data is updated directly (e.g. as a state of MyPage).
As it is now, when accessing http://localhost:3000/siteX/ the server should fetch the data and prerender the site
I could of course treat data as a state of <MyPage> and simply call a function to update it with a request when <Link> is clicked. But I also want a correct routing, history and so on.
Can you help me with that? Thank you!
After some more resesarch I came across a solution. The next.js <Link> component has a property shallow that I can set to prevent the execution of getServersideProps. That way I can query the new data manually when a link is clicked. Still the initial data query is done by getServersideProps on the server and my intial SSR is working as before.
// Filename: [mypath].jsx
export default function MyPage(props) {
const [data, setData] = useState(props.data);
function updateData(path) {
const res = await fetch(`https://external-api/${path}`)
const data = await res.json();
setData(data);
}
return (
<>
<!-- Manually query and set data -->
<div onClick={() => updateData("siteX")}>
<!-- Trigger routing, but don't execute getServerSideProps (because of shallow={true}) -->
<Link href="/siteX" shallow={true}>
<a>Go to siteX</a>
</Link>
</div>
<div onClick={() => updateData("siteY")}>
<Link href="/siteY" shallow={true}>
<a>Go to siteY</a>
</Link>
</div>
<div>{props.data.text}</div>
</>
);
}
// If page is requested directly via url (and not because a <Link> element has been clicked) do SSR as before
export async function getServerSideProps(context) {
const mypath = context.params.mypath;
const res = await fetch(`https://external-api/${mypath}`)
const data = await res.json();
return { props: { data } };
}