Is there a better and cleaner method other than getInitialProps - next.js

I have a nextJS site that has a custom _app.js file with the following code.
I have dynamic content from a cms that i use getServerSide props from for each of the pages I have.
const MyApp = ({ Component, pageProps }) => {
const { global } = pageProps
if (global == null) {
return <ErrorPage statusCode={404} />
}
if(global.attributes === undefined){
return <Component />
}
const { metadata, favicon, metaTitleSuffix } = global.attributes
return (
<>
{/* Favicon */}
<Head>
<link
rel="shortcut icon"
href={getStrapiMedia(favicon.data.attributes.url)}
/>
</Head>
{/* Global site metadata */}
<DefaultSeo
titleTemplate={`%s | ${metaTitleSuffix}`}
title="Page"
description={metadata.metaDescription}
/>
{/* Display the content */}
<Component {...pageProps} />
</>
)
}
MyApp.getInitialProps = async (appContext) => {
const appProps = await App.getInitialProps(appContext)
const globalLocale = await getGlobalData(appContext.router.locale)
return {
...appProps,
pageProps: {
global: globalLocale,
},
}
}
export default MyApp
Two questions in a way.
A. Do I even need this getInitialProps?
B. Is there a better way of using getInitialProps
Let me know if anymore code is needed

Related

Cannot destructure property 'docComponentsRendered' of '(0 , _react).useContext(...)' as it is null

I'm trying to run a custom document as it says into nextjs docs,
but the app throws an error when I run it. The error is
TypeError: Cannot destructure property 'docComponentsRendered' of '(0 , _react).useContext(...)' as it is null.
enter image description here
Anyway, this is my custom document code:
import Document, {
Html,
Head,
Main,
NextScript,
DocumentContext,
DocumentInitialProps
} from 'next/document'
import { ServerStyleSheet } from 'styled-components'
export default class MyDocument extends Document {
static async getInitialProps(
ctx: DocumentContext
): Promise<DocumentInitialProps> {
const sheet = new ServerStyleSheet()
const originalRenderPage = ctx.renderPage
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />)
})
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
)
}
} finally {
sheet.seal()
}
}
render() {
return (
<Html lang="pt-br">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
I'm running nextjs 13, and trying to configure the styled component.
Could some help me to solve this error?
I already tried to delete the _document, but now the error persists, and I have no idea what is the problem.

How to enable ssg/ssr in next.js with custom App component

I have a custom _app.js which looks like this:
function MyApp({ Component, pageProps }) {
const Layout = Component.layoutProps?.Layout || React.Fragment
const layoutProps = Component.layoutProps?.Layout
? { layoutProps: Component.layoutProps }
: {}
const meta = Component.layoutProps?.meta || {}
const description =
meta.metaDescription || meta.description || 'Meta Description'
const store = useStore(pageProps.initialReduxState)
return (
<QueryClientProvider client={queryClient}>
<Provider session={pageProps.session}>
<Title suffix="My Dynamic Site">{meta.metaTitle || meta.title}</Title>
<Description>{description}</Description>
<Meta />
<ReduxProvider store={store}>
<PersistGate persistor={store.__PERSISTOR} loading={null}>
<CartBox />
<Layout {...layoutProps}>
<Component {...pageProps} />
</Layout>
</PersistGate>
</ReduxProvider>
<ReactQueryDevtools initialIsOpen={false} />
</Provider>
</QueryClientProvider>
)
}
MyApp.getInitialProps = async (appContext) => {
// calls page's `getInitialProps` and fills `appProps.pageProps`
const appProps = await App.getInitialProps(appContext);
return { ...appProps }
}
export default MyApp
Now, I would like to fetch data using ssg/ssr data fetching method to help SEO team for my page components.
But, it seems any of the methods aren't working as expected, none of them actually passing props to the page component.
Here's my page component.
const HomePage = ({ title, stars }) => {
console.log(title, stars); // undefined, undefined
return (
<div>
<Header title={title} />
<GhStars stars={stars} />
<Footer />
</div>
)
}
export const getStaticProps = async () => {
return {
props: {
title: "My Dynamic Title From getStaticProps"
}
}
}
// I tried both getInitialProps & getStaticProps independently.
HomePage.getInitialProps = async (ctx) => {
const res = await fetch('https://api.github.com/repos/vercel/next.js')
const json = await res.json()
return { stars: json.stargazers_count }
}
export default HomePage
I might be missing something for sure, which I failed to figure out so far.
Any help will be really much appreciated. Thanks.

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;

How to make my scripts work on my components in next.js?

I have a problem with my script, I am using next and React in my pages / _app.js file I have the following code:
import axios from "axios";
import { $ } from "jquery";
import App from "next/app";
import Router from "next/router";
import { destroyCookie, parseCookies } from "nookies";
import Layout from "../components/_App/Layout";
import "../public/css/boot.css";
import "../public/css/icons.css";
import "../public/css/themes/style.css";
import "../public/jquery";
import "../public/scripts";
import baseUrl from "../utils/baseUrl";
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
const { token } = parseCookies(ctx);
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
if (!token) {
const isProtectedRoute =
ctx.pathname === "/profile" ||
ctx.pathname === "/admin/add-product" ||
ctx.pathname === "/my-orders-history" ||
ctx.pathname === "/admin/users" ||
ctx.pathname === "/admin/dashboard";
if (isProtectedRoute) {
redirectUser(ctx, "/auth/login");
}
} else {
try {
const payload = { headers: { Authorization: token } };
const url = `${baseUrl}/api/account`;
const response = await axios.get(url, payload);
const user = response.data;
const isRoot = user.role == "root";
const isAdmin = user.role == "admin";
// if authenticated but not root or admin
const isNotPermitted =
!(isRoot || isAdmin) &&
(ctx.pathname === "/admin/add-product" ||
ctx.pathname === "/admin/customers" ||
ctx.pathname === "/admin/orders" ||
ctx.pathname === "/admin/dashboard");
if (isNotPermitted) {
redirectUser(ctx, "/products");
}
pageProps.user = user;
} catch (error) {
// console.error("Error getting current user", error);
//invalid token
destroyCookie(ctx, "token");
redirectUser(ctx, "/auth/login");
}
}
return { pageProps };
}
componentDidMount() {
window.addEventListener("storage", this.syncLogout);
require($)(window);
}
syncLogout = e => {
if (e.key === "logout") {
Router.push("/");
}
};
render() {
const { Component, pageProps } = this.props;
return (
<Layout {...pageProps}>
<Component {...pageProps} />
</Layout>
);
}
}
export default MyApp;
In my components/_App/Layouts.js tenho o seguinte codigo:
import React from "react";
import Head from "next/head";
import Footer from "./Footer";
import StaticHeader from "./StaticHeader";
const Layout = ({ children, user }) => {
return (
<React.Fragment>
<Head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WdpShoes | Home</title>
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght#400;700;800&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="./public/css/icons.css" />
<link href="https://file.myfontastic.com/gMNiHf8HU5GG4r6y622k2N/icons.css" rel="stylesheet" />
<link rel="stylesheet" href="/css/boot.css" />
<link rel="stylesheet" href="/css/themes/style.css" />
<link rel="shortcut icon" href="/css/themes/logo/favicon.png" />
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
</Head>
<body>
<StaticHeader user={user} />
{children}
<Footer />
<script src="/jquery.js"></script>
<script src="/scripts.js"></script>
<script src="/css/themes/script.js"></script>
</body>
</React.Fragment>
);
};
export default Layout;
In the Components that are in my Layout the jquery and javascript are working but in my components they are not
When I import my script into my pages / _app.js file I encounter the following error:
ReferenceError: $ is not defined
at Object../public/scripts.js (C:\Users\walter\Desktop\mystore.next\server\pages_app.js:5217:1)
I looked for some materials and I didn't find anything that could help me solve this problem that is preventing me from developing several projects
I know the purpose of the site is to answer questions but to better explain my problem I have the complete code at:
https://codesandbox.io/s/dawn-mountain-vme5e?file=/pages/_app.js
And in its current state it encounters the Internal Server Error due to the fact that the $ function is returning an undefined,Because my script and jquery are not working correctly in my components
To understand your issue, a little theory is needed. Next.js uses server-side rendering and static generation, which means the all code is initially run on the server. jQuery is a client-side library and it can't run on the server. Therefore, you need to ensure that all the jQuery code is executed only on the client side. You can do this by checking if the window object exists and only then executing jQuery. Also, you've forgotten to include jQuery in your scripts.js file.
Modify the
scripts.js file:
import $ from "jquery";
if (typeof window !== "undefined") {
$(function () {
// Rest of the code...
}
}
Next, modify the componentDidMount function in _app.js:
componentDidMount() {
window.addEventListener("storage", this.syncLogout);
}

How to change default landing page to login instead of index

I am pretty new to next.js and kind of stuck on basic issue.
here is my _app.js
class MyApp extends App {
constructor (props) {
super(props)
this.persistor = persistStore(props.store)
}
static async getInitialProps ({ Component, ctx }) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps({ ctx })
}
return { pageProps }
}
render () {
const { Component, pageProps, store } = this.props
return (
<Provider store={store}>
<PersistGate
loading={null}
persistor={this.persistor}
>
<Component {...pageProps} />
</PersistGate>
</Provider>
)
}
}
export default withRedux(createStore)(withReduxSaga(MyApp))
This is my index.js
function Home (props) {
return (
<div>
<div className='home'>
<Link href='/login'>
<a>Go to Login</a>
</Link>
<h1>THIS IS HOME</h1>
</div>
}
Currently when I start an app it lands on index page.
How do I make this land on Login page directly?
Let me know if you need to see other files?
not sure if this is the right way of doing things but I was able to navigate user on Login page instead of index page when website loads.
// redirect user to login page
useEffect(() => {
Router.push('/login')
})
return (
<div>
</div>
)

Resources