react query and ssr of next.js is not working - server-side-rendering

emphasized texti have some problem applying ssr.
it's my code
const [queryClient] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: {
cacheTime: 1000 * 60 * 5, // 5 minute defualt
},
},
}),
);
return (
<QueryClientProvider client={queryClient}>
<Head>
<link rel="icon" type="image/x-icon" href="/favicon.svg" />
</Head>
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
</Hydrate>
<ReactQueryDevtools initialIsOpen={true} />
</QueryClientProvider>
export const getServerSideProps: GetServerSideProps = async (): Promise<{
props: { dehydratedState: DehydratedState };
}> => {
const queryClient = new QueryClient();
await queryClient.prefetchQuery(["ururlInfoQuery"], requestChallenge);
return { props: { dehydratedState: dehydrate(queryClient) } };
};
const Banner = function Banner(): JSX.Element {
const { data, isFetching } = useUrUrlInfoQuery();
const { challengeData } = ChallengeStore;
}
export const useUrUrlInfoQuery = () => {
return useQuery<ChallengeResponseType, Error | void>(["ururlInfoQuery"],
requestChallenge, {
staleTime: StaleTimeEnum.Default,
});
};
result:
defulat value: 3, 5
real value : 1, 10
this means serverSide render is not working.
data getted is init value i setted.
please, some comment help me

Related

React Query with firebase returns undefined

I am very new to state management libraries and the one that I chose was React Query. I have followed multiple tutorials about it with mock data, but when I try to use my firebase imported data, it returns undefined. I am attaching all of my code with instances of React Query in it. This app is in next js 12.
file where I want to fetch and render data
const fetchDbData = async () => {
const {currentUser} = UseAuth();
function getLoc() {
if (currentUser) {
return 'users/' + currentUser.uid + '/userInvestments'
} else {
return 'users/TestDocumentForDatabase/userInvestments'
};
}
const loc = getLoc();
const q = query(collection(db, loc));
const snapshot = await getDocs(q);
console.log(snapshot)
const {data, status, error} = useQuery(
['firebaseData'],
() => snapshot.forEach(doc => doc.data()),
{
refetchOnWindowFocus: false
},
{
retry: false
},
)
return {data, status, error}
}
export default function usePortfolio() {
const {data, status, error} = fetchDbData
.....
_app.js
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
useInfiniteQury: false,
},
}
})
export default function App({ Component, pageProps }) {
return (
<QueryClientProvider client={queryClient}>
<AuthProvider >
<RootLayout>
<Component {...pageProps} />
</RootLayout>
</AuthProvider>
</QueryClientProvider>
)}

Component is being rendered twice in SSG page in Next.js

I'm currently trying using utterances, which is a github-based open source for comments.
I'm using utterances in my SSG page. Therefore, I'm using client side rendering for getting the utterances component.
Here is the code.
// blog/[id].tsx
/* eslint-disable react/no-danger */
import axios from 'axios';
import { dateFormat } from '_Utils/Helper';
import MarkdownRenderer from '_Components/MarkdownRenderer';
import Comment from '_Components/Comment';
import styles from './blog.module.scss';
const Article = ({ article }: any) => {
return (
<div className={styles.container}>
<div className={styles.header}>
<p className={styles.tag}>{article.data.attributes.tag.data.attributes.tag}</p>
<h1>{article.data.attributes.title}</h1>
<p className={styles.publishedDate}>Published at {dateFormat(article.data.attributes.publishedAt)}</p>
</div>
<main
>
<MarkdownRenderer markdown={article.data.attributes.content} />
<Comment />
</main>
</div>
);
};
export async function getStaticPaths() {
const articlePaths: any = await axios.get(`${process.env.NEXT_PUBLIC_BASE_URL}/api/articles/?populate[0]=*`);
const paths = articlePaths.data.data.map((path: any) => ({
params: { id: `${path.id}` },
}));
return { paths, fallback: false };
}
export async function getStaticProps(ctx: any) {
const { params } = ctx;
const { id } = params;
const article = await axios.get(
`${process.env.NEXT_PUBLIC_BASE_URL}/api/articles/${id}?populate[1]=tag&populate[0]=thumbnail`
);
return {
props: { article: article.data },
};
}
export default Article;
// Comment
const Comment = () => {
return (
<section
style={{ height: '350px', width: '100%' }}
ref={(elem) => {
if (!elem) {
return;
}
const scriptElem = document.createElement('script');
scriptElem.src = 'https://utteranc.es/client.js';
scriptElem.async = true;
scriptElem.setAttribute('repo', 'usernamechiho/Cobb-dev-blog');
scriptElem.setAttribute('issue-term', 'title');
scriptElem.setAttribute('theme', 'github-light');
scriptElem.setAttribute('label', 'comment');
scriptElem.crossOrigin = 'anonymous';
elem.appendChild(scriptElem);
}}
/>
);
};
export default Comment;
and the result
I was wondering why it happens and tried dynamic import with ssr: false.
However, there was nothing but the same.
Is there anything I can look for to get through this?

infinite scroll with get static props and grapql not working

I'm working on a project with pokeapi graphql and I made a infinite scroll component that loads more pokemon when you scroll the page. I wanted to have the first 48 pokemons pre loaded with static generation.
So I have my index page the following code:
const Home = ({ fallback }): JSX.Element => {
return (
<div>
<SWRConfig value={fallback}>
<PokemonList />
</SWRConfig>
</div>
);
};
export const getStaticProps: GetStaticProps = async () => {
const url =
'species: pokemon_v2_pokemonspecies(order_by: {id: asc}, limit: 24, offset: 0)';
const pokemonList = await getPokemonListData({
url,
});
return {
props: {
fallback: {
'species: pokemon_v2_pokemonspecies(order_by: {id: asc}, limit: 24, offset: 0)':
pokemonList,
},
},
revalidate: 60 * 60 * 24, // 24 hours
};
};
And I use this custom hook for the data:
import getPokemonListData from '#requests/getPokemonListData';
import useSWRInfinite from 'swr/infinite';
interface IUsePokemonListParams {
limit?: number;
}
interface IUsePokemonListReponse {
pokemonList: IBasicPokemonInfo[][];
isLoading: boolean;
size: number;
setSize: (
size: number | ((_size: number) => number),
) => Promise<IBasicPokemonInfo[][]>;
}
export default function usePokemonList({
limit,
}: IUsePokemonListParams): IUsePokemonListReponse {
const getKey = (pageIndex, previousPageData) => {
if (previousPageData && !previousPageData.length) return null; // reached the end
return `species: pokemon_v2_pokemonspecies(order_by: {id: asc}, limit: ${limit}, offset: ${
pageIndex * limit
})`;
};
const { data, error, size, setSize } = useSWRInfinite(getKey, url =>
getPokemonListData({ url }),
);
return {
pokemonList: data,
isLoading: !error && !data,
size,
setSize,
};
}
on my list component I use the custom hook and list the data in another component:
const PokemonList = (): JSX.Element => {
const loader = useRef(null);
const { pokemonList, setSize } = usePokemonList({ limit: 24 });
useController({ loader, setSize }); // this is my infinite scroll logic, I set the size when I reach the page limit
useEffect(() => {
document.body.className = 'initial';
}, []);
return (
<>
<ol className={styles.cardContainer}>
<>
{pokemonList.map((list, index) => (
<Fragment key={index}>
{list.map(pokemon => (
<li key={pokemon.id}>
<Pokemon
id={pokemon.id}
name={pokemon.name}
types={pokemon.types}
image={pokemon.image}
/>
</li>
))}
</Fragment>
))}
</>
</ol>
<div ref={loader} />
</>
);
};
However, when I call the custom hook on my list component, for some reason the data returned from the hook, in this case the "pokemonList", is undefined and the request has to be made again. Is there something that I'm missing?
You have a mistake in your SWRConfig. Instead of <SWRConfig value={fallback}> you should have <SWRConfig value={{fallback}}>

Next.js: Can't deploy production build; Works in dev; Am I using the async functions correctly?

I am trying to create a production build for a next.js project. However I am getting the following error.
Error occurred prerendering page "/products". Read more: https://nextjs.org/docs/messages/prerender-error
TypeError: Cannot destructure property 'productList' of 'data' as it is undefined.
at ProductTile (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/jcrew-take-home-assignment/.next/server/chunks/619.js:39:5)
at d (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/jcrew-take-home-assignment/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:33:498)
at bb (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/jcrew-take-home-assignment/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:36:16)
at a.b.render (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/jcrew-take-home-assignment/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:42:43)
at a.b.read (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/jcrew-take-home-assignment/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:41:83)
at exports.renderToString (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/jcrew-take-home-assignment/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:52:138)
at Object.renderPage (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/jcrew-take-home-assignment/node_modules/next/dist/next-server/server/render.js:53:854)
at Function.getInitialProps (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/jcrew-take-home-assignment/.next/server/pages/_document.js:599:19)
at loadGetInitialProps (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/jcrew-take-home-assignment/node_modules/next/dist/next-server/lib/utils.js:5:101)
at renderToHTML (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/jcrew-take-home-assignment/node_modules/next/dist/next-server/server/render.js:53:1145)
errors FetchError: request to http://localhost:8000/category-server/ failed, reason: connect ECONNREFUSED 127.0.0.1:8000
at ClientRequest.<anonymous> (/Users/antonio-pavicevac-ortiz/Dropbox/developer_folder/jcrew-take-home-assignment/node_modules/node-fetch/lib/index.js:1461:11)
at ClientRequest.emit (node:events:327:20)
at Socket.socketErrorListener (node:_http_client:478:9)
at Socket.emit (node:events:327:20)
at emitErrorNT (node:internal/streams/destroy:194:8)
at emitErrorCloseNT (node:internal/streams/destroy:159:3)
at processTicksAndRejections (node:internal/process/task_queues:80:21) {
type: 'system',
errno: 'ECONNREFUSED',
code: 'ECONNREFUSED'
}
I believe the crux of it is:
Error occurred prerendering page "/products". Read more: https://nextjs.org/docs/messages/prerender-error
But I can't figure it out—here are the asnyc files in questions:
[product].js
import React, { useEffect, useState } from 'react'
import Image from 'next/image'
import Link from 'next/link'
import Swatches from '../../components/Swatches.js'
function Product({ query }) {
let { defaultColorCode, description, colors, product, price } = query;
colors = JSON.parse(colors)
let [productImage, setProductImage] = useState(`/${product}_${defaultColorCode}`);
const handleProductChange = (productCode, defaultColorCode) => {
setProductImage(`/${productCode}_${defaultColorCode}`)
}
const myLoader = ({ src }) => {
return `https://www.jcrew.com/s7-img-facade/${src}`
}
return (
<div className="p-10 grid grid-cols-1 sm:grid-cols-1 md:grid-cols-1 lg:grid-cols-2 xl:grid-cols-2 gap-5">
<div className="product-link p-2 rounded overflow-hidden shadow-lg">
<Image className="product-image" loader={myLoader}
src={productImage} alt={description} layout="fill" />
<p className="font-medium text-sm mb-2">{description}</p>
<p className="text-sm text-gray-700">
{
price
}
</p>
</div>
<span className="p-3 font-medium text-sm mb-2">
<Swatches handleProductChange={handleProductChange} defaultColorCode={defaultColorCode} productImage={productImage} productCode={product} colors={colors} />
</span>
</div>
)
}
export const getServerSideProps = ({ query }) => {
return {
props: {
query
}
}
}
export default Product
The home page:
import Head from 'next/head'
import ProductPage from './products.js'
export const getStaticProps = async () => {
try {
const res = await fetch('http://localhost:8000/category-server/')
const data = await res.json()
return {
props: {
data: data
},
}
} catch (error) {
console.log('errors', error)
}
}
export default function Home({ data }) {
return (
<>
<Head>
<title>Category Page 🤔</title>
<meta charSet="utf-8" />
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>
<ProductPage data={data} />
</>
)
}
Update:
I wound up updating the /server/category-server.js file:
const express = require('express')
const next = require('next')
const PORT = parseInt(process.env.PORT, 10) || 8000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = express();
server.get('https://jcrew-take-home-assignment.herokuapp.com/category-server', function (req, res) {
res.json(require('./category'))
})
server.all('*', (req, res) => {
return handle(req, res)
})
if (process.env.NODE_ENV === 'production') {
server.use(express.static('.next/'));
server.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, '.next/', 'index.html'));
});
server.listen(PORT, err => {
if (err) throw err;
console.log(
`> Ready and listening on PORT:${PORT} in the ${process.env.NODE_ENV} environment`
);
});
} else {
server.listen(PORT, err => {
if (err) throw err;
console.log(`> Ready and listening on http://localhost:${PORT}`);
});
}
My pages/index.js
import Head from 'next/head'
import absoluteUrl from 'next-absolute-url'
import dynamic from 'next/dynamic'
const ProductPage = dynamic(
() => import('./products.js'),
{ ssr: false }
)
const fetchRelative = (req, path) => {
const { origin } = absoluteUrl(req)
return fetch(`${origin}${path}`);
}
export const getStaticProps = async ({ req, res }) => {
if (typeof (window) !== 'undefined') {
var res = await fetchRelative(req, '/category-server/')
var data = await res.json()
}
return {
props: {
data: data || {}
},
}
}
export default function Home({ data }) {
return (
<>
<Head>
<title>Category Page 🤔</title>
<meta charSet="utf-8" />
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>
<ProductPage data={data} />
</>
)
}
And my ProductTile.js
import React from 'react'
import Image from 'next/image'
import Link from 'next/link'
const ProductTile = ({ data = {} }) => {
let { productList } = data
var [products] = productList || []
var { products } = products || {};
React.useEffect(() => {
function remove_style(all) {
var i = all.length;
var j, is_hidden;
// Presentational attributes.
var attr = [
'align',
'background',
'bgcolor',
'border',
'cellpadding',
'cellspacing',
'color',
'face',
'height',
'hspace',
'marginheight',
'marginwidth',
'noshade',
'nowrap',
'valign',
'vspace',
'width',
'vlink',
'alink',
'text',
'link',
'frame',
'frameborder',
'clear',
'scrolling',
'style'
];
var attr_len = attr.length;
while (i--) {
is_hidden = (all[i].style.display === 'none');
j = attr_len;
while (j--) {
all[i].removeAttribute(attr[j]);
}
if (is_hidden) {
all[i].style.display = 'none';
is_hidden = false;
}
}
}
var all = window.document.getElementsByTagName('*');
remove_style(all);
}, [])
const myLoader = ({ src }) => {
return `https://www.jcrew.com/s7-img-facade/${src}`
}
const getColorSwatches = (products, index) => {
if (products[index] != undefined) {
return JSON.stringify(products[index].colors)
}
}
return (
<div>
<div className="p-10 grid grid-cols-1 sm:grid-cols-1 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-3 gap-5">
{
products != undefined && products.reduce((products, product) => products.find(x => x.productId === product.productId) ? products : [...products, product], []).map(({ colorCode, defaultColorCode, now, productId, productCode,
productDescription, }, index) => {
let path = `https://www.jcrew.com/s7-img-facade/${productCode}_${defaultColorCode}`
let swatches = getColorSwatches(products, index)
return (
<Link key={`${productId}${index}`}
href={{
pathname: '/s7-img-facade/[slug]',
query: {
defaultColorCode: defaultColorCode,
slug: productCode,
description: productDescription, image: path, price: now !== undefined
? now.formatted
: 'Not-available',
colors: swatches
},
}}
passHref>
<div className="product-link p-2 rounded overflow-hidden shadow-lg">
<Image className="product-image" loader={myLoader}
src={`/${productCode}_${defaultColorCode}`} alt={productDescription} layout="fill" />
<p className="font-medium text-sm mb-2">{productDescription}</p>
<p className="text-sm text-gray-700">
{
now !== undefined
? now.formatted
: 'Not-available'
}
</p>
</div>
</Link>
)
})}
</div>
</div>
)
}
export default ProductTile

next-i18next with next-rewrite does not work with root page rewrite path

We have an existing app, where the root "/" gets redirected to "/search" by default. It's been working fine via our next-redirects.js file:
async function redirects() {
return [
{
source: '/',
destination: '/search',
permanent: true,
},
];
}
I have to implement translation to the app, using next-i18next, so that we can have translated text + routing out of the box with NextJS. I have followed the steps in the next-i8next docs. I added the next-i18next.config.js file as:
const path = require('path');
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'es'],
},
localePath: path.resolve('./public/static/locales'), // custom path file route
};
And the next.config looks like:
const { i18n } = require('./next-i18next.config');
const defaultConfig = {
env: {
SOME_ENV,
},
images: {
deviceSizes: [980],
domains: [
'd1',
'd2',
],
},
i18n,
redirects: require('./next-redirects'),
webpack: (config, options) => {
if (!options.isServer) {
config.resolve.alias['#sentry/node'] = '#sentry/browser';
}
if (
NODE_ENV === 'production'
) {
config.plugins.push(
new SentryWebpackPlugin({
include: '.next',
ignore: ['node_modules'],
urlPrefix: '~/_next',
release: VERCEL_GITHUB_COMMIT_SHA,
})
);
}
return config;
},
};
module.exports = withPlugins([withSourceMaps], defaultConfig);
We have a custom _app file getting wrapped with the appWithTranslation HOC, and it's setup with the getInitialProps, per nextJS docs:
function MyApp({ Component, pageProps }) {
const [mounted, setMounted] = useState(false);
useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentNode.removeChild(jssStyles);
}
TagManager.initialize(tagManagerArgs);
setMounted(true);
}, []);
const Layout = Component.Layout || Page;
return (
<>
<Head>
<link rel="icon" href="/favicon.png" type="image/ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<AppProviders>
<Context {...pageProps}>
<Layout {...pageProps}>
<>
<Component {...pageProps} />
<Feedback />
<PageLoader />
</>
</Layout>
</Context>
</AppProviders>
</>
);
}
MyApp.getInitialProps = async ({ Component, ctx }) => {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps({ ctx });
}
const cookies = Cookie.parse(ctx?.req?.headers?.cookie || '');
if (Object.keys(cookies).length) {
const { token } = JSON.parse(cookies?.user || '{}');
let user = null;
if (token) {
const { data } = await get('api/users', { token });
if (data) {
user = data;
user.token = token;
}
}
pageProps.user = user;
pageProps.cart = cookies?.cart;
pageProps.defaultBilling = cookies?.defaultBilling;
pageProps.reservationEstimateItem = cookies?.reservationEstimateItem;
pageProps.reservationEstimate = cookies?.reservationEstimate;
}
return { pageProps };
};
export default appWithTranslation(MyApp);
And we have our _document file to handle some Emotion theming:
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
const styles = extractCritical(initialProps.html);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
<style
data-emotion-css={styles.ids.join(' ')}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: styles.css }}
/>
</>
),
};
}
render() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
<script
type="text/javascript"
src="https://js.stripe.com/v2/"
async
/>
</body>
</Html>
);
}
}
// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with server-side generation (SSG).
MyDocument.getInitialProps = async ctx => {
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: App => props => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
};
};
At this point the redirect logic should continue to navigate to the search page which is setup like so:
export const SearchPage = () => {
const router = useRouter();
const { t } = useTranslation('search');
return (
<>
<Head>
<title>{`${t('searchTitle')}`}</title>
<meta
property="og:title"
content={`${t('searchTitle')}`}
key="title"
/>
<meta
name="description"
content={t('metaDescription')}
/>
</Head>
<Search />
</>
);
};
SearchPage.namespace = 'SearchPage';
export const getStaticPaths = () => ({
paths: [], // indicates that no page needs be created at build time
fallback: 'blocking' // indicates the type of fallback
});
export const getStaticProps = async ({ locale }) => ({
// exposes `_nextI18Next` as props which includes our namespaced files
props: {
...await serverSideTranslations(locale, ['common', 'search']),
}
});
export default SearchPage;
The search page has the getStaticPaths & getStaticProps functions, as needed on ALL page level files, per next-i18next.
Why does this setup no longer work with the redirect?
There are no errors in the terminal.
The network tab shows a 404 error on the root route of "/"
which implies the re-writing is not working. But what about the i18n makes this not behave?
Is it something in the _app or _document files?
If I navigate to /search directly, it loads fine, so the page routes are OK it seems.
Other notes:
NextJS "next": "^10.0.2",
next-i18next "next-i18next": "^7.0.1",
There seems to be some possible issues with Next & Locales...
https://github.com/vercel/next.js/issues/20488
https://github.com/vercel/next.js/issues/18349
My workaround is not pretty, but it works:
Delete the original next-rewrite
Add a new index.js page file that handles the redirect in the getServerSideProps:
const Index = () => null;
export async function getServerSideProps({ locale }) {
return {
redirect: {
destination: `${locale !== 'en' ? `/${locale}` : ''}/search`,
permanent: true,
},
};
}
export default Index;

Resources