document is not defined when creating new portal in Nextjs - reactjs

in my project I want to separate section like menu in header tag, content in main tag and contact in footer tag _document.js file, for this I created a new portal but I get an error document is not defined in my Header.js file.
I trying to create new portal in nextjs like this:
import React, { Fragment } from "react";
import ReactDOM from "react-dom";
import NavigationBar from "./NavigationBar";
import classes from "./header.module.css";
const Header = (props) => {
return (
<Fragment>
{ReactDOM.createPortal(
<NavigationBar>{props.children}</NavigationBar>,
document.getElementById("header")
)}
</Fragment>
);
};
export default Header;
I created _document.js:
import Document, { Html, Head, Main, NextScript } from "next/document";
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}
render() {
return (
<Html lang="en" dir="ltr">
<Head />
<body>
<header id="header"></header>
<Main />
<footer id="_footer"></footer>
<div id="_modal"></div>
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;

The error is thrown because document is only available inside the browser and not on the server. Next js executes this code on the server side and that's why the error is thrown.
You can wrap our code inside an if statement. If you check on the console the type of window it returns object. The following code illustrates this:
if (typeof window === 'object') {
// Check if document is finally loaded
}

Use React portal maker:
import React from "react";
import reactDom from "react-dom";
const Header = (props) => {
const content = <NavigationBar>{props.children}</NavigationBar>;
if (typeof window === "object") {
return reactDom.createPortal(content, document.getElementById("__header"));
}
return null;
};
export default Header ;

Related

How to use query param as a variable in groq sanity query inside getServerSideprops in next js?

I'm trying to implement search fuctionality in my blog project but facing problem in using variable in groq query(sanity) inside getServersideprops.
Here is my code
import React from 'react'
import { useRouter } from 'next/router'
import type { NextPage } from 'next'
import Head from 'next/head'
import Image from 'next/image'
import Banner from '../../components/Banner'
import Header from '../../components/Header'
import { sanityClient,urlFor } from '../../sanity'
import { Post } from '../../typings'
import PostCard from '../../components/Post'
import Footer from '../../components/Footer'
import BottomNav from '../../components/BottomNav'
interface Props{
posts:[Post];
}
export default function Search({ posts}:Props) {
return (
<>
<div className='max-w-6xl mx-auto container-fix md:mb-1 '>
<Head>
<title>Const - Read for free</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<Header/>
<Banner/>
<PostCard posts={posts} />
<Footer/>
</div>
<BottomNav />
</>
)
}
export const getServerSideProps = async(context:any)=>{
const search = context.query.url;
const query = `
*[_type=="post" && title == $search]{
_id,
title,
author -> {
name,
image,
},
_createdAt,
description,
mainImage,
category,
slug
}`;
const posts = await sanityClient.fetch(query);
return {
props:{
posts
}
}
}
this is the error im getting
**Server Error
ClientError: param $search referenced, but not provided
**
but when the variable is repalced with actual string then I'm geeting results succesfully.
groq query doesnot pick the variable directly from declaration .need to pass the variable in sanityclient.fetch() alongside the query.
const posts = await sanityClient.fetch(query,{search:search});

Next JS with react-intl creates an ancestry issue

I'm trying to setup NEXT Js with react-intl.
I've added to my _app.tsx the setup for react-intl as follows:
import { IntlProvider } from 'react-intl';
import English from "../../lang/compiled-locales/en-US.json"
import French from "../../lang/compiled-locales/fr.json"
import configs from '../../configs';
// https://itnext.io/next-js-react-intl-add-internationalisation-to-your-nextjs-app-4f933104b1f7
const MyApp = ({ Component, pageProps }: AppProps) => {
const { locale } = useRouter()
const messages = useMemo(() => {
switch (locale) {
case "fr":
return French
default:
return English
}
}, [locale])
return <IntlProvider
locale={locale || configs.languages.default}
messages={messages}>
<Component {...pageProps} />
</IntlProvider>
}
export default MyApp
When I ran it, it worked like a charm, but when I do a page reload, I get the following error:
Server Error
Error: [React Intl] Could not find required `intl` object. <IntlProvider> needs to exist in the component ancestry.
This error happened while generating the page. Any console logs will be displayed in the terminal window.
I'm guessing it's something to do with SSR but no idea how to fix. Thank you in advance for your help.
Here is the sample usage in my pages/index.tsx
import type { NextPage } from 'next'
import Head from 'next/head'
import { FormattedMessage } from 'react-intl'
import Header from '../components/Header'
const Home: NextPage = () => {
return <div className="">
<Head>
<title><FormattedMessage defaultMessage="FlipFlip - Print your videos for real!" id="landing.seo.title" /></title>
</Head>
<Header />
</div>
}
export default Home

Rendering multiple components in React js

Community, I rendered more than one component from a single page and the problem I receive is:
[./src/App.js Attempted import error: 'Greeting' is not exported from './components/Home'.]
Can anybody tell me why?
App.js
import "./App.css";
import { Home, Page, Greeting } from "./components/Home";
function App() {
return (
<div className="App">
<Home />
<Page />
<Greeting />
</div>
);
}
export default App;
Home.js
import React, { Component } from "react";
import React from "react";
class Home extends Component {
render() {
return (
<div className="box">
<h1>Its a box man!</h1>
</div>
);
}
}
class Page extends Component {
render() {
return (
<div className="box">
<h1>Its a second box man! from the other Component</h1>
</div>
);
}
}
const Greeting = () => {
return <h1>Hello again</h1>;
};
export { Home, Page, Greeting };
*The aim to practice two components from the same page, rather than just separating them.
Try to export all components one by one in Home.js like this:
export class Home...
export class Page...
export const Greeting...
And after that delete this line:
export { Home, Page, Greeting };
Change
const Greeting = () => {
return <h1>Hello again</h1>;
};
to
export const Greeting = () => {
return <h1>Hello again</h1>;
};
and your issue should be resolved
Try this in Home.js. If you export module as default, you can import without parenthesis {}. But if you export without default, you have to import using parenthesis.
export default { Home, Page, Greeting };
I tested your code and there was no problems except if these are not typos:
you haven't imported react in your App.js
you've imported react twice in your Home.js
The only problem I can think of is that you have forgotten to save Home.js.
If you've saved Home.js:
exit you're dev server using ctrl + c and start it again and see if the problem is resolved.
try removing Greeting Component and let us know if you still get errors.

Next.js 9+ FOUC (Flash or Unstyled Content) with Styled Components

Have spent a couple days on this issue sourcing solutions and ideas from most of SA and Reddit however to no avail..
Upon load both in production and local every load presents the whole html without any styling before then being injected into DOM..
Currently this is my projects key files:
_document.js
import { ServerStyleSheet } from "styled-components";
import Document, { Main, NextScript } from "next/document";
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
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="en">
<Head>
<title>namesjames</title>
<link rel="icon" href="/favicon.ico" />
<script src="/static/chrome-fix.js" />
<link href="/above-the-fold.css" />
</Head>
<body>
<script src="/noflash.js" />
<Main />
<NextScript />
<script> </script>
</body>
</html>
);
}
}
_app.js
/* eslint-disable class-methods-use-this */
import App from "next/app";
import React from "react";
import { ThemeProvider } from "styled-components";
import { ParallaxProvider } from 'react-scroll-parallax';
import Header from "../components/Header";
import theme from "../theme";
import GlobalStyles from "../GlobalStyles";
import DarkModeToggle from '../components/toggle/toggleMode';
import Footer from '../components/Footer'
import LazyLoad from 'react-lazy-load';
import Router from 'next/router';
import styled from 'styled-components'
// import '../style.css'
const Loaded = styled.div`
opacity: ${(props) => props.loaded ? "1" : "0"};
`
export default class MyApp extends App {
state = { isLoading: false, loaded: false }
componentDidMount() {
// Logging to prove _app.js only mounts once,
// but initializing router events here will also accomplishes
// goal of setting state on route change
console.log('MOUNT');
this.setState({loaded: true})
Router.events.on('routeChangeStart', () => {
this.setState({ isLoading: true });
console.log('loading is true, routechangeStart')
});
Router.events.on('routeChangeComplete', () => {
this.setState({ isLoading: false });
console.log('loading is false, routeChangeComplete')
});
Router.events.on('routeChangeError', () => {
this.setState({ isLoading: false });
console.log('loading is false, routeChangeError')
});
}
render(): JSX.Element {
const { isLoading } = this.state;
const { Component, pageProps, router, loaded } = this.props;
return (
<Loaded loaded={this.state.loaded}>
<ThemeProvider theme={theme}>
<GlobalStyles />
<ParallaxProvider>
<Header />
{isLoading && 'STRING OR LOADING COMPONENT HERE...'}
<Component {...pageProps} key={router.route} />
<LazyLoad offsetVertical={500}>
<Footer />
</LazyLoad>
</ParallaxProvider>
<DarkModeToggle />
</ThemeProvider>
</Loaded>
);
}
}
index.js
import { color } from "styled-system";
import { OffWhite } from "../util/tokens";
import Hero from "../components/Hero";
import Banner from '../components/Banner'
import TitleText from '../components/TitleText'
import HomeTitleCopyScene from '../components/HomeTitleCopyScene'
import TwoCards from '../components/TwoCards'
import LazyLoad from 'react-lazy-load';
function Home(): JSX.Element {
return (
<div className="container">
<Banner />
<HomeTitleCopyScene />
<LazyLoad offsetVertical={1000}>
<TwoCards />
</LazyLoad>
</div>
);
}
export default Home;
As some might see I have tried multiple implementations and am just a bit confused as to what it could be at this stage..
Any help appreciated and I can provide more information upon request.. Many thanks
I found two solutions which helped -->
Was to hard style opacity:0 into the JSX and then upon styling injecting into DOM applying opacity: 1 !important onto any of the components displayed..
<section className="cards-block" style={{opacity:0}}>
Whilst this was effective this morning I discovered at some point during my development I had incorrectly imported Head from next/head and used this in my _document.js rather than using the correct Head from next/documents..
// import Head from "next/head"; --> incorrect
import { ServerStyleSheet } from "styled-components";
import Document, { Head, Main, NextScript } from "next/document";
Ergo -> a correctly rendering and injected element with no FOUC
Hope this helps someone out there
I've found a workaround for my small portfolio project:
just included this inline css to a <head> of my custom _document.js:
{<style dangerouslySetInnerHTML={{__html: `
html {background: #333}
body #__next div {visibility: hidden}
body.loaded #__next div {visibility: visible}
`}}></style>}
The "loaded" class is added to the body in _app.js:
if (process.browser) {
document.body.classList.add("loaded")
}
I'm hot sure if it's a good solution, any advice would be appreciated :)

Intense FOUC when using Next.js 8 and styled-jsx

I have recently upgraded to Next.js 8.0.3 from 6.1.1 and I am now encountering a very intense flash of un-styled content (FOUC) for my header content which is using styled-jsx. It loaded just fine before updating Next.js.
The header code that is flashing is a custom built npm module that uses styled-jsx (but not next) and is being imported and placed into a layout page that is rendered with every next page.
This was the implementation in the _document.js file before updating next and it was working:
import Document, { Head, Main, NextScript } from 'next/document'
import { ServerStyleSheet, injectGlobal } from 'styled-components'
import styledNormalize from 'styled-normalize'
import flush from 'styled-jsx/server'
injectGlobal`
${styledNormalize}
`
export default class MyDocument extends Document {
static getInitialProps({ renderPage }) {
const sheet = new ServerStyleSheet()
const page = renderPage(App => props =>
sheet.collectStyles(<App {...props} />)
)
const styleTags = sheet.getStyleElement()
const styles = flush()
return { ...page, styleTags, styles }
}
render() {
return (
<html>
<Head>
{this.props.styleTags}
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
Based on the docs I have also tried this (where I wait for the initial props):
import Document, { Head, Main, NextScript } from 'next/document'
import { ServerStyleSheet, injectGlobal } from 'styled-components'
import styledNormalize from 'styled-normalize'
import flush from 'styled-jsx/server'
injectGlobal`
${styledNormalize}
`
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet()
const page = ctx.renderPage(App => props =>
sheet.collectStyles(<App {...props} />)
)
const initialProps = await Document.getInitialProps(ctx)
const styleTags = sheet.getStyleElement()
const styles = flush()
return { ...initialProps, ...page, styles, styleTags }
}
render() {
return (
<html>
<Head>
{this.props.styleTags}
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
The flash might be a result of where I am implementing the module but not sure.
It seems like the code coming in from the module is not being properly bundled with the rest of the pages and thus giving the page flash. Any thoughts or feedback would be appreciated.
I ended up fixing the issue by refactoring the custom npm module to not use styled-jsx but instead use styled-components. Not really a fix but more of a work around

Resources