Next.js Webviewer Reference Error on build, window is not defined - javascript

I am creating a website where I have a pdf viewer inside my page imported dynamically.
the code runs locally without errors, but during the "npm run build" command I find the following error:
> Build error occurred
ReferenceError: window is not defined
at Object.<anonymous> (C:\Users\GustavoMorilla\MyProfile\node_modules\#pdftron\webviewer\webviewer.min.js:1:224)
at Object.g7Pv (C:\Users\Gustavo Morilla\MyProfile\.next\server\pages\resume\viewer.js:121:18)
at __webpack_require__ (C:\Users\Gustavo Morilla\MyProfile\.next\server\pages\resume\viewer.js:23:31)
at Module.vRHl (C:\Users\Gustavo Morilla\MyProfile\.next\server\pages\resume\viewer.js:134:76) {
type: 'ReferenceError'
info - Collecting page data .npm ERR! code ELIFECYCLE }
viewer.js code:
import React, { useRef, useEffect } from "react";
import WebViewer from "#pdftron/webviewer";
const Viewer = () => {
const viewer = useRef(null);
useEffect(() => {
WebViewer(
{
path: '/lib',
initialDoc: "/pdf/GustavoMorilla.pdf",
},
viewer.current
).then(function (instance) {
instance.setTheme("dark");
});
}, []);
return (
<div>
<div className="Viewer">
<div className="header flex justify-between">
<a>Resume</a>
<a className="text-sm">You can download the file on "Settings" button</a>
</div>
<div className="Webviewer" ref={viewer}></div>
</div>
</div>
);
};
export default Viewer;
resume.js code:
import SiteLayout from "../../components/SiteLayout";
import React from "react";
import dynamic from "next/dynamic";
const Viewer = dynamic(() => import("../resume/viewer.js"), { ssr: false });
export default function Resume({ resume }) {
return (
<div>
<SiteLayout>
<div className="main w-screen">
<Viewer />
</div>
</SiteLayout>
</div>
);
}
should be something related with the SSR or lifecycle... i dont know.

window object doesn't exist on the server side, use conditional rendering to solve the problem.
import SiteLayout from '../../components/SiteLayout'
import React from 'react'
import dynamic from 'next/dynamic'
const Viewer = dynamic(() => import('../resume/viewer.js'), { ssr: false })
export default function Resume({ resume }) {
return window ? (
<div>
<SiteLayout>
<div className="main w-screen">
<Viewer />
</div>
</SiteLayout>
</div>
) : null
}

Related

Is there a way to import a function using Next.js dynamic import? react-component-export-image issues with Next.js ssr

I was getting the 'window is not defined' error when importing react-component-export-image so I used a dynamic import to get around that. I don't get that error anymore but now I get 'exportComponentAsPNG(componentRef) is not a function'. Is there a better way to deal with the 'window is not defined' error or a way to use the function I am importing dynamically? If not, is there a different npm library that works to generate an image from a react component?
import React, { useRef } from 'react'
// import { exportComponentAsPNG } from 'react-component-export-image' *This gave window not defined error so I used dynamic import*
import dynamic from 'next/dynamic'
import ProductCard from '../ProductCard/ProductCard.component'
import Button from '../Button/Button.component'
const { exportComponentAsPNG } = dynamic(
() => import('react-component-export-image'),
{
ssr: false
}
)
const Plaque = () => {
const componentRef = useRef()
// eslint-disable-next-line react/display-name
const ComponentToPrint = React.forwardRef((props, ref) => {
return (
<div ref={ref}>
<ProductCard />
</div>
)
})
return (
<ComponentToPrint ref={componentRef} />
<button onClick={() => exportComponentAsPNG(componentRef)}> // "Error: exportComponentAsPNG is not a function"
Export As PNG
</button>
)
}
export default Plaque
next/dynamic is used to dynamically import React components, not regular JavaScript functions or libraries.
For that, you can use a regular dynamic import on exportComponentAsPNG inside the onClick callback.
<button onClick={async () => {
const { exportComponentAsPNG } = await import('react-component-export-image')
exportComponentAsPNG(componentRef)
}}>
The exportComponentAsPNG function needs access to window which is undefined with server side rendering. I was able to fix the issue by dynamically importing the Plaque component that used exportComponentAsPNG to the page where it is called with sever side rendering set to 'false'.
import dynamic from 'next/dynamic'
const Plaque = dynamic(() => import('../compnonents/Plaque'), {
ssr: false
})
const Page = () => {
return <Plaque />
}
export default Page
Now that the component is no longer using SSR I was able to import and use the function normally.
import { exportComponentAsPNG } from 'react-component-export-image'
Here you can find the documentation for the library: https://www.npmjs.com/package/react-component-export-image

Next js 404 page not working in tsx extension

I have created a next js project with normal javaScript.This is my 404 page
404.js
import { useEffect } from "react";
import { useRouter } from "next/router";
import Link from "next/link";
export default function NotFound() {
const router = useRouter();
useEffect(() => {
setTimeout(() => {
// router.go(1)
router.push("/");
}, 3000);
}, []);
return (
<div className="not-found">
<h1>Oooops!!!</h1>
<p>
Go back to home page
<Link href="/">
<a>Home page</a>
</Link>
</p>
</div>
);
}
Then I created a seperate type script based next js project.
404.tsx
// import type { NextPage } from 'next'
import Link from "next/link";
const NotFound = () => {
return (
<div>
<h2>Oh no!</h2>
<p>There is not much left here for you</p>
{/* <Link href="/">
<a >Visit our home page</a>
</Link> */}
</div>
);
};
export default NotFound;
This is what I get in the typescript project.
What is wrong in the 404.tsx file?
It says that client_dev_noop_js module loading failed. There is nothing with 404.tsx, however, it is better to name _error.js and _error.tsx

How to use vanta with nextjs?

I am trying to use vanta with next.js, following this guide. It works completely fine with the Net Effect, however, when I try to use the Globe Effect, I get
[VANTA] Init error TypeError: r.Geometry is not a constructor
at h.onInit (vanta.globe.min.js:1)
at h.init (vanta.globe.min.js:1)
at new r.VantaBase (vanta.globe.min.js:1)
at new h (vanta.globe.min.js:1)
at r.<computed> (vanta.globe.min.js:1)
I have isolated Vanta into an Background Component
//Background.js
import { useState, useRef, useEffect } from "react";
import NET from "vanta/dist/vanta.globe.min"
import * as THREE from "three";
export default function Background({ width, height, children }) {
const [vantaEffect, setVantaEffect] = useState(0);
const vantaRef = useRef(null);
useEffect(() => {
if (!vantaEffect) {
setVantaEffect(
NET({
THREE,
el: vantaRef.current,
})
);
}
return () => {
if (vantaEffect) vantaEffect.destroy();
};
}, [vantaEffect]);
return (
<div ref={vantaRef}>{children}</div>
)
}
And added the THREE script into my _app.js
import '../styles/globals.css'
import Head from "next/head";
import Navbar from "../components/Navbar";
import { useEffect } from "react";
export default function App({ Component, pageProps }) {
useEffect(() => {
const threeScript = document.createElement("script");
threeScript.setAttribute("id", "threeScript");
threeScript.setAttribute(
"src",
"https://cdnjs.cloudflare.com/ajax/libs/three.js/r121/three.min.js"
);
document.getElementsByTagName("head")[0].appendChild(threeScript);
return () => {
if (threeScript) {
threeScript.remove();
}
};
}, []);
return (
<>
<Head>
<title>BrainStorm Tutoring</title>
</Head>
<Navbar />
<Component {...pageProps} />
</>
)
}
and used it like so
//index
import Background from "../components/Background";
export default function Home() {
return (
<Background height="400" width="400">
<h1 className="text-white text-8xl text-left p-36">Fish Bowl</h1>
</Background >
)
}
Is it something wrong with THREE, or is it that next.js can't support vanta?
I have that issue with Halo, so i think the THREE object was not available or was not defined in the HALO.js file.
So i go to the official github repo of Vanta and take the source of Halo and Net (the tutorial effect) file, and i found constructor was missing in the Halo file. So i take the one of Net and put in the Halo file.
constructor(userOptions) {
THREE = userOptions.THREE || THREE;
super(userOptions);
}
Then i import my custom Halo file for the effect and it works.
I was playing around with this and found that, if I keep the Three.js version to 122. I don't get the error. Apparently any version after that has a breaking change.

How to solve: PDF installed instead of opening with react-pdf?

Hi, I'd like to display multiple pages of my PDF. I've followed the code sample on react-pdf. However, when I run the site on localhost, the PDF is installed rather than opened.
Please help
1. Error:
2. Folder structure:
3. What I've tried:
Include the media queries
Try all suggested file path (public, import, relative)
4. Here's my code:
import React from "react";
import { Page } from "react-pdf";
import { pdfjs } from "react-pdf";
import { Document } from "react-pdf/dist/esm/entry.webpack";
import Lesson_1 from "./Lesson 1.pdf";
// import {} from '../../../public/docs/pdf/Lesson 1.pdf'
// pdfjs.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
const options = {
cMapUrl: "cmaps/",
cMapPacked: true,
};
function Lesson(props) {
const [numPages, setNumPages] = React.useState(null);
const [pageNumber, setPageNumber] = React.useState(1);
function onDocumentLoadSuccess({ numPages }) {
setNumPages(numPages);
}
return (
<div>
<Document
file="Lesson 1.pdf"
options={options}
onLoadSuccess={onDocumentLoadSuccess}
>
<Page pageNumber={pageNumber} />
</Document>
<p>
Page {pageNumber} of {numPages}
</p>
</div>
);
}
export default Lesson;

react-loadable component does not wait trigger to load

This is a basic header component that includes buttons to show login/register forms on click.
The intention is, of course, that the login and register components shouldn't be loaded until requested.
react-loadable is creating an additional file ( 0.js ) that seem to include the register component, but my search did not turn out any reference to login.
In any case, upon initial load, both login and register are being loaded, as my console.log shows.
Of course, I was expecting that they would not be loaded until the triggering button was clicked on.
Note that I did attempt to use react-loadable on routes, and it seems to work correctly, ie, I can see the files being loaded on the network tab of the dev tools.
Also, I happen to have a service worker precaching the build files for now, but I do not believe that should impact this. Or should it? It doesn't on routes.
Excerpts of the header component:
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { withRouter } from 'react-router-dom';
import Loadable from 'react-loadable';
//import Register from '../register/'; ** PREVIOUS REGULAR IMPORT
//import Login from '../login/'; ** PREVIOUS REGULAR IMPORT
import { Loading } from '../../../tools/functions';
const Login = Loadable({
loader: () => import('../login/'),
loading: Loading,
delay: 200,
timeout: 5000
});
const Register = Loadable({
loader: () => import('../register/'),
loading: Loading,
delay: 200,
timeout: 10000
});
render () {
return (
<header>
<div className="header_top flex">
<div className="branding"></div>
<div className="header_spacer"></div>
<Status handleClick={this.handleLoginRegisterClick}/>
</div>
<Nav />
<div className="auth">
<Register
active={this.state.activeRegister}
handleClick={this.handleLoginRegisterClick}
toggleDialog={this.toggleLoginRegisterDialogs}
/>
<Login
active={this.state.activeLogin}
handleClick={this.handleLoginRegisterClick}
handleClickPasswordReset={this.togglePasswordResetRequest}
toggleDialog={this.toggleLoginRegisterDialogs}
/>
<PasswordResetRequest
active={this.state.activePasswordResetRequest}
hidePasswordResetRequest={this.togglePasswordResetRequest}
/>
<SessionHandler location={location}/>
</div>
{showAdForm()}
</header>
);
}
The Loading function:
export const Loading = ({ error }) => {
if (error) {
return 'error';
} else {
return <h3>Loading...</h3>;
}
}
My mistake was that I had the state change on the child component.
So, I factored it up, changing the render method of the header component as follows:
const activeDialog = () => {
if (this.state.activeLogin) {
return (
<Login
active={this.state.activeLogin}
handleClick={this.handleLoginRegisterClick}
handleClickPasswordReset={this.togglePasswordResetRequest}
toggleDialog={this.toggleLoginRegisterDialogs}
/>
)
} else if (this.state.activeRegister) {
return (
<Register
active={this.state.activeRegister}
handleClick={this.handleLoginRegisterClick}
toggleDialog={this.toggleLoginRegisterDialogs}
/>
)
} else if (this.state.activePasswordResetRequest) {
return (
<PasswordResetRequest
active={this.state.activePasswordResetRequest}
hidePasswordResetRequest={this.togglePasswordResetRequest}
/>
)
}
}
return (
<header>
<div className="header_top flex">
<div className="branding"></div>
<div className="header_spacer"></div>
<Status handleClick={this.handleLoginRegisterClick}/>
</div>
<Nav />
<div className="auth">
{activeDialog()}
<SessionHandler location={location}/>
</div>
{showAdForm()}
</header>
);

Categories

Resources