Nuxtjs The fetch hook doesn't happen at build time, it runs on the client - fetch

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>

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.

tRCP is fetching data again, even when it's fetched and provided in getServerSideProps

In my project I'm using NextJs and tRPC for backend calls. I wanted to fetch some data in getServerSideProps using tRPC and provide it in Page component, also using react-query state for whole application. Here's my _app withTRPC config
export default withTRPC<AppRouter>({
config({ ctx }) {
const url = `${getBaseUrl()}/api/trpc`;
return {
url,
transformer: superjson,
queryClientConfig: { defaultOptions: { queries: { staleTime: 60 } } },
};
},
ssr: false,
})(MyApp);
I used ssr: false because of 'bug' in NextJs, which will cause to return empty props for first render, if set to true.
Here's my getServerSideProps function on the page
export const getServerSideProps = async () => {
const ssg = createSSGHelpers({
router: appRouter,
ctx: await createContext(),
transformer: superjson,
});
const entryRD = await getPageBySlug(option.some("trados"))();
await ssg.prefetchQuery("contentful.getProductList");
console.log("==>props", entryRD, ssg.dehydrate());
return {
props: {
trpcState: ssg.dehydrate(),
entryRD,
},
};
};
When I log to the console on server, both values are there, entryRD and ssg.dehydrate(). The latter contains object with mutations and queries and also data with correctly fetched data. Here is my page code:
const Page = ({ entryRD, trpcState }: InferGetServerSidePropsType<typeof getServerSideProps>) => {
const { data, isLoading } = trpc.useQuery(["contentful.getProductList"]);
console.log("==>data", data, isLoading);
return isLoading ? <div>Loading...</div> : <EntryCompontn entry={entryRD} />
When I read the docs, I understand it like:
fetch data on server,
use ssg.dehydrate() to return cache to component
when you use trpc.useQueried(), it will return cached value from state
Unfortunately data is empty and isLoading is true for a brief moment, when data is fetched. Did I misunderstood something, or did I make a mistake?

NextJS SSR with context on refresh

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?

Optimize calls to exteral Api from getServerSideProps

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 } };
}

Firebase Storage XMLHttpRequest is not defined error with Nuxt JS

I am in need of help with my web app that is uploading an image to firebase storage then wanting to display that image in a thumbnail.
I am getting the error this.xhr_ = new XMLHTTPREQUEST is not defined
I don't have 10 rep points so it seems I can't upload my image directly.
If there was a better way to do this please let me know.
I have looked through firebase docs and many stack overflow answers but can seem to have anything that works. I have tried to globally install xmlhttprequest, also as a dependency.
As you can see I also attempted to import XmlHttpRequest but it did nothing.
The error I am getting comes from the last statement with getdownloadurl()
<script>
import XMLHTTPREQUEST from 'xmlhttprequest'
import ImageCard from '../../components/ImageCard/ImageCard'
import {db} from '../../firebase/init.js'
import {storage} from '../../firebase/init.js'
export default {
name: "explore",
components: {
ImageCard,
db,
storage,
XMLHTTPREQUEST
},
data() {
return {
cards: [],
downloadUrl: ''
}
},
created(){
//fetch data from firestore
db.collection('Assets').get()
.then(snapshot => {
snapshot.forEach( doc => {
let card = doc.data()
console.log(card)
// the card.id is adding an id property onto the let card variable
card.id = doc.id
this.cards.push(card)
console.log(this.cards)
})
})
},
created() {
const storageRef = storage.ref()
const imagesRef = storageRef.child('AshAngelPaid.jpg');
console.log('Before getting requesting download url')
imagesRef.getDownloadURL().then( (url) => {
document.querySelector('img').src = url;
console.log('got download url');
Basically, while nuxtjs is rendering your component on the server side there's no xmlhttprequest, just move .getDownloadURL and related stuff into mounted() or beforeMount() lifecycle hook.

Resources