NextJS - ReactDOMServer does not yet support Suspense - next.js

I'm currently trying to incorporate a loader component to a site built with NextJS. I would like to use Suspense to show a loading screen may it be after refreshing the page or changing routes.
This is how my code goes:
import Head from 'next/head'
import { Loader } from '../components/loader'
const { Suspense } = require('React')
function MyApp({ Component, pageProps }) {
return (
<>
<Suspense fallback={<Loader />}>
<Head>
.... some codes such as meta tags, title tags ....
</Head>
<Component {...pageProps} />;
</Suspense>
</>
)
}
My problem is I get an error that says ReactDOMServer does not yet support Suspense. but I would like to use Suspense to enable a loading screen on my page. Much like this website

You can use React 18 features like suspense in Next.js Advanced Features. Obviously it's still experimental and might cause issues with you application.
npm install next#latest react#rc react-dom#rc
To enable, use the experimental flag concurrentFeatures: true
// next.config.js
module.exports = {
experimental: {
concurrentFeatures: true,
},
}
Once enabled, you can use Suspense and SSR streaming for all pages.
import dynamic from 'next/dynamic'
import { lazy, Suspense } from 'react'
import Content from '../components/content'
// These two ways are identical:
const Profile = dynamic(() => import('./profile'), { suspense: true })
const Footer = lazy(() => import('./footer'))
export default function Home() {
return (
<div>
<Suspense fallback={<Spinner />}>
{/* A component that uses Suspense-based */}
<Content />
</Suspense>
<Suspense fallback={<Spinner />}>
<Profile />
</Suspense>
<Suspense fallback={<Spinner />}>
<Footer />
</Suspense>
</div>
)
}

I had a similar issue. I ended up simulating the Suspense with a combination of setState & componentDidMount
render(){
return this.state.browser ? <Component/> : <Placeholder/>
}
componentDidMount(){
this.setState({browser: true})
}
I hope it helps.

Related

Why the Script component inside the Head component of a Nextjs application blocks the dynamic insertion of css rules by the sweetalert library?

Below snippet is taken from _app.tsx file. I know that having Script component inside the Head component is wrong but this buggy code is blocking the sweetalert library (which I am using in another page to render pop up) from dynamically inserting the CSS rules.
import Head from "next/head";
import Script from "next/script";
import type { AppProps } from "next/app";
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Head>
<Script
id="branch-io"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `(function(){console.log("hello world")}())`,
}}
/>
</Head>
<Component {...pageProps} />
</>
);
}
For example in this page when I click on the button the popup will be displayed without any CSS applied to it.
import swal from "sweetalert";
export default function Home() {
return (
<button
onClick={() => swal("Hello world!")}
style={{
width: "25vw",
height: "25vh",
color: "white",
backgroundColor: "red",
}}>
Patronus Potter
</button>
);
}
I can resolve the bug by correcting the mistake in the _app.tsx file but my question is why does it happen? Not able to pin point an answer to the question. Any pointers on how to investigate further would be appreciated or Does anybody know why this happens?

Images not showing after adding const slider ... so confused

So i've been following a tutorial and trying to teach myself next.js with lazy loading slider and tailwindcss.. I am a beginner but i have made ecommerce sites and stuff to teach myself.. but this tutorial was going great until this!but Ive tried everything i know and googled it every which way to try to fix this issue!! My images were showing perfectly before i added the - const setCurrent up to the if !Array
if i delete that the images show back up... im so confused ive gone through everything - also the "Gallery" even disapeared once i added that section
and i put it all on github incase someone can look at it to help me?? https://github.com/Jessica19882/firebird
i have deleted the
const current up to the !Array part and images showed back up i have tried rewriting it as another post said but that didnt work ive checked and zoomed into the video tutorial to make sure everything was right and it is... i have checked all the other pages index.js app.js and stuff to make sure i had it just like his and it is!!
slider.js
import { SliderData } from './SliderData'
import React, { useState } from 'react'
import Image from 'next/image'
const Slider = ({ slides }) => {
const [current, setCurrent] = useState(0)
const length = slides?.length
const nextSlide = () => {
setCurrent(current === length - 1 ? 0 : current + 1)
}
const prevSlide = () => {
setCurrent(current === 0 ? length - 1 : current - 1)
}
if (!Array.isArray(slides) || slides.length <= 0) {
return null
}
return (
<div id='gallery'>
<h1>Gallery</h1>
<div>
{SliderData.map((slide, index) => {
return (
<div
key={index}
className={
index === current
? 'opacity-[1] ease-in duration-1000'
: 'opacity-0'
}>
<Image
src={slide.image}
alt='/'
width='1440'
height='600'
style={{ objectFit: 'cover' }}
/>
</div>
)
})}
</div>
</div>
)
}
export default Slider
index.js
import Head from 'next/head'
import Hero from '../components/Hero'
import Slider from '../components/Slider'
import SliderData from '../components/SliderData'
export default function Home() {
return (
<div>
<Head>
<title>Firebird Sounds</title>
<meta name='description' content='firebird sounds' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
<link rel='icon' href='/favicon.ico' />
</Head>
<Hero
heading='Firebird Sounds - Audio & Video Distribution'
message='Audio and Video Distribution with a menu of
Services: Marketing, PR, Global rights management and creativity'
/>
<Slider Slides={SliderData} />
</div>
)
}
SliderData.js
export const SliderData = [
{
image:
'https://images.unsplash.com/photo-1466428996289-fb355538da1b?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MTl8fG11c2ljJTIwZGlzdHJpYnV0aW9ufGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=500&q=60',
},
{
image: '/images/FirebirdSounds.jpg',
},
{
image:
'https://images.unsplash.com/photo-1460667262436-cf19894f4774?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NjZ8fG11c2ljJTIwZGlzdHJpYnV0aW9ufGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=500&q=60',
},
{
image: '/images/guitar.jpg',
},
{
image: '/images/firebird.png',
},
]
app.js
import Navbar from '../components/Navbar'
import '../styles/globals.css'
export default function App({ Component, pageProps }) {
return (
<>
<Navbar />
<Component {...pageProps} />
</>
)
}
https://www.youtube.com/watch?fbclid=IwAR2jl5qYoIsaFZQ26MumbFcCYv5t3eYxcqVKgm4xAsxilhEkAzSNoUB0fzE&v=HVyct9EUNP8&feature=youtu.be
I figured it out I needed to put the
Slider slides={SliderData}/>
into _app.js not index.js or both but its working again!

Why is my react-lightbox not going full screen in next.js

I'm using next.js and I was hoping to see my gallery collection like this on click of one of my images which is not happening. Actually, it's like I have just used a normal component, because literally nothing is happening when I click one of my images. Please help.
// this is my app component
import SimpleReactLightbox from 'simple-react-lightbox'
const MyApp = ()=>{
return(
<SimpleReactLightbox>
<Component {...pageProps} />
</SimpleReactLightbox>
)
}
// this is my collection
import { CollectionStyledTypography } from './styles/collectionStyledComponents'
import { SRLWrapper } from 'simple-react-lightbox'
import Image from 'next/image'
const Collection = ({ imagesList = [] }) => {
return (
<SRLWrapper>
<div style={{ margin: '50px' }}>
{imagesList.map((image, index) => (
<CollectionStyledTypography component="div" key={index}>
<Image src={image.src} alt={image.alt} layout="fill" />
</CollectionStyledTypography>
))}
</div>
</SRLWrapper>
)
}
export default Collection
SRL is no longer being developed, and it won't work in React 18. You will have to downgrade to 17 to make it work. I am facing the same problem, nothing happens when i click on an image.

React-tooltip and Next.js SSR issue

I use the react-tooltip library in my Next.js app.
I noticed that every time I refresh a website while visiting a page that uses the tooltip I get an error:
react-dom.development.js:88 Warning: Prop `dangerouslySetInnerHTML` did not match.
CSS classes are different on the client and on the server
The weird part is I do not get that error while navigating from a random page to a page that uses the react-tooltip.
The tooltip related code:
<StyledPopularityTooltipIcon src="/icons/tooltip.svg" alt="question mark" data-tip="hello world" />
<ReactTooltip
effect="solid"
className="tooltip"
backgroundColor="#F0F0F0"
arrowColor="#F0F0F0"
clickable={true}
/>
I had the same issue, I had to use state to detect when component has been mounted, and show the tooltip only after that.
P.S. You don't see the error when navigating, because the page is not rendered on server when you navigate, it's all front-end :)
In case you are using any server-side rendering (like Next.js) - you will need to make sure your component is mounted first before showing the react-tooltip.
I fixed this by using the following:
import React, { useEffect, useState } from 'react';
const [isMounted,setIsMounted] = useState(false); // Need this for the react-tooltip
useEffect(() => {
setIsMounted(true);
},[]);
return (<div>
{isMounted && <ReactTooltip id={"mytip"} effect={"solid"} />}
<span data-tip={"Tip Here"} data-for={"mytip"}>Hover me</span>
</div>)
You should wrap your JSX in the following component:
import React, { useEffect, useState } from 'react';
const NoSsr = ({ children }): JSX.Element => {
const [isMounted, setMount] = useState(false);
useEffect(() => {
setMount(true);
}, []);
return <>{isMounted ? children : null}</>;
};
export default NoSsr;
Like this:
<NoSsr>
<YourJSX />
</NoSsr>
If you are working with NEXTJS this might be a good approach, you can check the documentation here as well, also if you are working with data-event, globalEventOff or any other prop and is not hiding or not working in your localhost, this only occurs in Development Strict Mode. ReactTooltip works fine in Production code with React 18. So you can set reactStrictMode : false, in your next.config.js to test it locally and then set it back to true, hope this helps :) info reference here
import dynamic from 'next/dynamic'
const ReactTooltip = dynamic(() => import('react-tooltip'), { ssr : false });
function Home() {
return (
<div>
<Button
data-tip
data-event="click focus"
data-for="toolTip"
onClick={():void => ()}
/>
<ReactTooltip id="toolTip" globalEventOff="click"/>
</div>
)
}
export default Home

Dynamically load .css based on condition in reactJS application

I have a reactJS application that I want to make available to multiple clients. Each clients has unique color schemes. I need to be able to import the .css file that corresponds to the specific client.
For example, if client 1 logs into the application, I want to import client1.css. if client 2 logs into the application, I want to import client2.css. I will know the client number once I have validated the login information.
The application contains multiple .js files. Every .js file contains the following at the top of the file
import React from 'react';
import { Redirect } from 'react-router-dom';
import {mqRequest} from '../functions/commonFunctions.js';
import '../styles/app.css';
Is there a way to import .css files dynamically for this scenario as opposed to specifying the .css file in the above import statement?
Thank you
Easy - i've delt with similar before.
componentWillMount() {
if(this.props.css1 === true) {
require('style1.css');
} else {
require('style2.css');
}
}
Consider using a cssInJs solution. Popular libraries are: emotion and styled-components but there are others as well.
I generally recommend a cssInJs solution, but for what you are trying to do it is especially useful.
In Emotion for example they have a tool specifically build for this purpose - the contextTheme.
What cssInJs basically means is that instead of using different static css files, use all the power of Javascript, to generate the needed css rules from your javascript code.
A bit late to the party, I want to expand on #Harmenx answer.
require works in development environments only, once it goes to production you're likely to get errors or not see the css file at all. Here are some options if you, or others, encounter this:
Option 1: Using css modules, assign a variable of styles with the response from the import based on the condition.
let styles;
if(this.props.css1 === true) {
//require('style1.css');
import("./style1.module.css").then((res) => { styles = res;});
} else {
//require('style2.css');
import("./style2.module.css").then((res) => { styles = res;});
}
...
<div className={styles.divClass}>...</div>
...
Option 2: using Suspend and lazy load from react
// STEP 1: create components for each stylesheet
// styles1.js
import React from "react";
import "./styles1.css";
export const Style1Variables = (React.FC = () => <></>);
export default Style1Variables ;
// styles2.js
import React from "react";
import "./styles2.css";
export const Style2Variables = (React.FC = () => <></>);
export default Style2Variables ;
// STEP 2: setup your conditional rendering component
import React, {lazy, Suspense} from "react";
const Styles1= lazy(() => import("./styles1"));
const Styles2= lazy(() => import("./styles2"));
export const ThemeSelector = ({ children }) => {
return (
<>
<Suspense fallback={null} />}>
{isClient1() ? <Styles1 /> : <Styles2/>}
</Suspense>
{children}
</>
);
};
// STEP 3: Wrap your app
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
Option 3: Use React Helm which will include a link to the stylesheet in the header based on a conditional
class App extends Component {
render() {
<>
<Helmet>
<link
type="text/css"
rel="stylesheet"
href={isClient1() ? "./styles1.css" : "./styles2.css"}
/>
</Helmet>
...
</>
}
}
Personally, I like option 2 because you can set a variable whichClientIsThis() then modify the code to:
import React, {lazy, Suspense} from "react";
let clientID = whichClientIsThis();
const Styles= lazy(() => import("./`${clientID}`.css")); // change your variable filenames to match the client id.
export const ThemeSelector = ({ children }) => {
return (
<>
<Suspense fallback={null} />}>
<Styles />
</Suspense>
{children}
</>
);
};
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
This way you don't need any conditionals. I'd still recommend lazy loading and suspending so the app has time to get the id and make the "decision" on which stylesheet to bring in.

Resources