NextJS - ReferrenceError: document is not defined [duplicate] - javascript

This question already has answers here:
Window is not defined in Next.js React app
(23 answers)
Closed 1 year ago.
I'm running NextJS v11.1.12, and am trying to run a standard Vanilla JS function that toggles the CSS class of the div with the class "state-select-dropdown-box". My code is:
const Header = () => {
const dropDownMenu = document.querySelector('state-select-dropdown-box');
console.log(dropDownMenu);
function toggleMenu() {
dropDownMenu.classList.toggle('show');
}
return ( <Component /> );
}
When the app compiles, I get "Referrence Error: document is not defined". What's weird is I have got these functions running earlier today with no issues whatsoever and not changing a thing.
After some mass Googling and research, I've come to the conclusion that maybe I'm just not understanding how SSR works in NextJS? Could anyone explain or shine some light on why I'm not achieving the results that I'm expecting and how this can be solved?

You are getting this error because document object is not available on the server. Similarly, if you try to use window, localStorage then also it will throw the error.
These are only available on client-side(browser).
NextJs serves your pages from the server. So in your case it is trying to get document on the server, but document object is not available on the server. Hence you are getting the error.
To get rid of this error:
You need to specify that such code should be run on browser side rather than on the server.
So for this, you can use typeof document
So your code should be like this:
const dropDownMenu = typeof document !== 'undefined' && document.querySelector('state-select-dropdown-box');
Ref: https://github.com/vercel/next.js/issues/5354#issuecomment-520305040

#Surjeet Bhadauriya answer is technically correct, however next.js provide built in functionality to dynamically load components, in fact with next/dynamic you can load a component withoutSSR.
From the docs :
You may not always want to include a module on server-side. For
example, when the module includes a library that only works in the
browser.
const Header = () => {
const dropDownMenu = document.querySelector('state-select-dropdown-box');
console.log(dropDownMenu);
function toggleMenu() {
dropDownMenu.classList.toggle('show');
}
return ( <Component /> );
}
export default Header
Then in your page (or wherever you import it) :
import dynamic from 'next/dynamic'
const DynamicComponentWithNoSSR = dynamic(
() => import('../components/Header'),
{ ssr: false } // <-- not including this component on server-side
)

Related

A React, Vue, or other code depends on an object, not yet available. The best ways to wait for that variable, before rendering component content?

When an HTML document defined a variable that is not available until a later time during the page load.
Issue: A React, Vue, or other block of code depends on an object that has not yet been declared and outside the direct scope of the component, like window['varname']. What are the proper way(s) to wait for that variable to be defined before rendering a component's real content.
My Attempt:
import React from 'react'
import ReactDOM from 'react-dom/client'
import AppWrapper from "./components/AppWrapper";
const App = () => {
let intervalId
intervalId = setInterval(() => {
console.log('Waking up... checking if window.app is defined')
if (window['app'] !== undefined) {
console.log('yes')
clearInterval(intervalId)
} else {
console.log('no')
}
}, 1000)
if(app.ins.length === 0) {
return 'Loading...'
}
return (
<AppWrapper app={window['app']}></AppWrapper>
)
}
export default App
What other ways could you, should you, do it?
I will make it clearer for you :) I will describe exactly my problem: So I am writing a custom Joomla Component for Joomla. The Joomla Component is written in PHP and uses the Joomla Framework. Inside this component, I have written a Reactjs component. The way that you inject JavaScript into Joomla is via Joomla Methods. These methods either load the JS on the head of the document or in the body. Now, my Reactjs component is loaded during this process. This is fine and it works as long as I do not need to rely on outside variables.
I am using Joomla to store data that is need by the Reactjs component. The way that Joomla makes data available to JS is by a Joomla library that will inject the JS object into a script tag. This is also okay. The issue is that when the head tag loads the Reactjs component before the injected JS object, needed by the Reactjs component, is available. In my example above I store the global JS object into the window object as window.app = Some Object. Because the window.app object is not available at the time the Reactjs component has been loaded, I add a setInterval and check every 500 ms.
Then the setInterval wakes up and checks to see if the window["app"] is available yet. It keeps doing that until it is available. Once it is, it quits the interval and loads the Reactjs component container, passing in the required object.
Now, two things here:
I have no way of synchronizing this process in Joomla. Joomla is stubborn like that.
This is my attempted to only load the Reactjs container component once the data is available.
Question: Knowing the situation, what are the best strategies to accomplish this, apart from my current strategy?
Thanks :)
I believe, one of the approaches could be any kind of callback or subscription.
For example, you can define a function, which changes a state in state-container like redux.
(Pseudocode)
async function loadAppData(store) {
const data = await fetch('/some-data');
const json = await data.json();
store.dispatch('data-loaded', json)
}
And in component
function App() {
const appData = useSelector(store => store.appData);
if (!appData) {
return 'Loading...'
}
return <Markup />
}
Other option can be subscription. Again you can add some function which emits some event:
async function loadAppData(store) {
const data = await fetch('/some-data');
const json = await data.json();
eventBus.emit('data-loaded', json)
// or
window.appData = json
}
In react you can
function App() {
const [appData, setAppData] = useState();
useEffect(() => {
setAppData(window.appData)
}, [window.appData])
if (!appData) {
return 'Loading...'
}
return <Markup />
}
Or in Vue.js you could
data() {
return {
appData: ''
}
}
mounted() {
this.$on('data-loaded', this.onDataLoaded)
}
methods: {
onDataLoaded($event) {
this.appData = $event;
}
}

Use custom JavaScript code in a Vue.js app

I'm trying to insert JavaScript code in a Vue.js router app. I need to load data from the CMS the app is served from. In order to get the data from the CMS I have to use a JavaScript library from the CMS which is not made for Vue and is not exporting it's class/functions like modern JS. So I import the JS library from in the index.html by a script tag. This works as intended.
But now I have to use the class from this CMS JavaScript library.
Before writing this as a Vue-Router app I just have used Vue for templating purposes.
So I had some code packed in the window.onload event handler.
I have to create an instance for the CMS data access class.
But this leads to a build error (using vue-cli build). Since there
are no understandable error messages from the build process
I have to use trial and error. Even simple variable assignments like var a = 1 seem not to be allowed.
A console.log('something') works. But nothing else seemes to be allowed (except defining the onload-event handler)
I have added this code in a <script> Tag inside App.vue (which was created by vue-cli create)
window.onload = function() {
try {
// Instantiate class obj for CMS data access
cmsDataAccessObj = new CMSAccessData();
waitForPlayerData = true;
}
catch (e) {
console.error(e);
}
}
UPDATE
After testing the different solutions from the answers I got aware that using non-instance variables seems to cause the build errors.
This gives an error:
waitForPlayerData = true;
This works:
this.waitForPlayerData = true;
I wouldn't recommend using window.load to run your code. There are more native approaches to do this in Vue.js.
What you should do in the case you want to run it in the main component of the app before it's been loaded is to put the code inside the beforeCreate lifecycle hook of the main component.
...
beforeCreate () {
this.cmsDataLoader()
},
methods: {
cmsDataLoader () {
try {
// Instantiate class obj for CMS data access
cmsDataAccessObj = new CMSAccessData();
waitForPlayerData = true;
}
catch (e) {
console.error(e);
}
}
}
...
This will run the code everytime a component is created before the creation. You could also use the created lifecycle hook if you want to run it after the creation of the component.
Check the following link for more information about lifecycle hooks.
The best way to place JavaScript in Vue.js App is mounted function, it is called when the component is loaded:
export default {
name: "component_name",
mounted() {
let array = document.querySelectorAll('.list_item');
},
}
You don't need window.onload, you can just put whatever you want there. I'm not entirely certain when precisely in the lifecycle it renders and maybe someone can hop in and let us know but it for sure renders when page starts. (though it makes sense that it does before the lifecycle hooks even start and that it'll solve your needs)
Better & easier solution if you want to load it before Vue loads is to add it to the main.js file. You have full control there and you can load it before Vue initializes.
No need for window.onload there either, just put it before or import a JS file before you initialize Vue, because it's going to be initialized by order.

Gatsby & React Context API: Using Context to set languages for internationalization

Currently working on a Gatsby project and attempting to implement i18n/internationalization. I want to serve English and French versions of my site.
I'm following along with this tutorial to implement this. I'm running into a problem when it comes to using the Context API to create context and pass it to my components. In the tutorial I'm following there is an odd syntax that I'm unsure of:
const PageContext = React.createContext<PageContextValue>({})
Here is the full component in which the context is created using createContext:
import React from 'react'
import { useTranslation } from 'react-i18next'
const PageContext = React.createContext<PageContextValue>({})
export const PageContextProvider = ({ pageContext, children }) => {
const { i18n } = useTranslation()
i18n.changeLanguage(pageContext.lang)
return <PageContext.Provider pageContext={pageContext}>{children}</PageContext.Provider>;
}
export const usePageContext = () => React.useContext(PageContext)
It produces the following error:
What is causing this error? I don't know much about the Context API but the React.createContext<PageContextValue>({}) syntax looks strange and throws an error.
The createContext is a generic method, so the PageContextValue is the type of your context's value.
The tutorial doesn't seem to define this value (which is an issue in my opinion), but you could do so by:
type PageContextValue = {
// Your type definition here
}
However, typescript is smart enough to infer the type itself, so you can just create the context:
const PageContext = React.createContext({})
Note that the type is static once it's defined, so if you actually pass a {} that'll be the set type and you wont be able to actually add values to your context, otherwise you'll receive typescript errors. So make sure to:
Either define the type as shown above
Or add default values when creating the context to make sure typescript infers it correctly, for example: const PageContext = React.createContext({counter: 0}) would allow you to access counter on your context later without an error.
Edit: Just figured out you are not using typescript. The tutorial you've posted is written in typescript! You'll stumble upon more errors if you continue using its syntax so I suggest you just use typescript for your project. But just for this error, it can be fixed removing the generic type (<PageContextValue>)

Expected server HTML to contain a matching <tag> in <tag>

We are using nextjs and getting this error at page refresh (or first load)
My error is:
react-dom.development.js:88 Warning: Expected server HTML to contain a matching <tag> in <tag>.
The code of our functional component looks like this:
export default MyComponent () {
if(! props.something){ // ← this is causing the problem.
return null;
}
return (
<>
HTML here ...
</>
)
}
From my understanding, SSR is different from client side rendering and this is why react is complaining.
The app is working fine but this error is showing in the console and we don't want to have many errors being thrown there as this may prevent us from seeing the real errors when they happens.
Solution:
The solution is to use dynamic imports and and wrap the component call into:
const MyDynamicComponent = dynamic(() => import('./myComponent'), {ssr: false});
//use it:
<MyDynamicComponent />
//OR :
const MyDynamicComponent = dynamic(() => import('./myComponent'))
//use it:
{typeof window !== 'undefined' && (
<MyDynamicComponent />
)}
May be importing your component dynamically should solve this.
Below is the link you can refer;
https://nextjs.org/docs/advanced-features/dynamic-import
I've been looking for solution of a similar problem, but I was using react-window lib and the problem appeared to be in it. I passed different height to it, depends on whether it's loading on server or client, so it gave me back different elements.
My solution was to pass the same height at the beginning, but to change it to window.innerHeight on useEffect ( = on client load).
When this happened to me on a Next project, I had not nested my html table elements properly. Specifically, didn't wrap my <tr> in a <tbody>
the code is executed first on the server after in the browser in my case this is why i get this error and other error with style component like :
Warning: Prop className did not match. Server: "CardUserInfo....
so this link help :
https://github.com/vercel/next.js/discussions/17443
enter link description here
and the solution for me like montienned in this disscussion implement useState useEffect
const [mount, setMount] = useState(false)
useEffect(() => {
setMount(true)
}, [ ])
return mount&&(
<InfoUserCard/>)
In my case the console error displaying Warning: Expected server HTML to contain a matching <sup> in <span>. using Safari Web Browser in development ONLY, i.e. localhost:3000 . Just get rid of this boring error by using Chrome Web Browser.
This boring error will not be exist in production with Safari Web Browser.

Cannot read property 'getHostNode' of null

I have a horizon/react app with react router and I have a simple button in my app:
<Link className="dark button" to="/">Another Search</Link>
When I click on it, I get the following exception:
Uncaught TypeError: Cannot read property 'getHostNode' of null
The error comes from:
getHostNode: function (internalInstance) {
return internalInstance.getHostNode();
},
Any idea why am I getting this?
I was facing a similar issue. It turns out that, in my case, was highlighthjs removing comments from the generated dom.
For text, React 15 is adding comment with the reactid instead of a span tag, as in:
<!-- react-text: 248-->
Another Search
<!--/react-test-->
Can you try something like this?
<Link className="dark button" to="/"><span>Another Search</span></Link>
This will force the generated DOM to include the span with the proper data-reactid attribute.
I would file an issue with react-router, maybe they can do that internally so you would not have to bother about it. But there are challenges with that as the Link child could be basically anything.
I have run into this issue multiple times in the last few days (new to react) and in almost all the cases, there was some syntax/code error that was not caught anywhere else. One example: If you write:
getInitialState() {
showModal: false
},
instead of:
getInitialState() {
return {
showModal: false
};
},
you will get this error. That is unless your build process does not already catch the error. Hope this helps someone (or myself in a couple of days. Hi Niyaz, you are welcome!).
If anyone else finds this thread. For me this turned out to be a null error for a prop.
Code generating error:
<Menu InventoryCount={this.state.inventoryModel.length} />
Working null checked code:
<Menu InventoryCount={this.state.inventoryModel ? this.state.inventoryModel.length : 0} />
For me, it's a typo which results in importing component from wrong module.
import { Link, Icon } from 'react-router';
import { Tag } from 'antd';
it should be
import { Link } from 'react-router';
import { Tag, Icon } from 'antd';
I just had to restart my nodemon backend.
Very interesting :) for me, it turned out that I was consuming props incorrectly in child component. Might be helpful for someone.
function Parent(){
const styleEle = { width: '100px'};
return (<div>
<Child styleO={styleEle}/>
</div>);
}
function Parent(props){
// here i was directly using <div style={styleO}> causing issue for me
return (<div style={props.styleO}>
{'Something'}
</div>)
}
if you getting error like "getHostNode" of null then its a error related to old code which is written before and it comes with version update of react
we have two ways to resolve the same
1) First we have to uninstall react from project and than again install react with the version specified( old one 15.4.2) current version of react is 15.6.1
2) Second way is bit time consuming but for future of application its good , go through the old code and handle errors(error handling of promises ) with the correct way following are few links which help you to figure out whats running behind
https://github.com/facebook/react/issues/8579
https://github.com/mitodl/micromasters/pull/3022
I got this error trying to render undefined value by mistake.
render(){
let name = this.getName(); // this returns `undefined`
return <div>name: {name}</div>
}
The fix is to fallback to null (where is accepted value)
render(){
let name = this.getName(); // this returns `undefined`
return <div>name: {name || null}</div>
}
I have had similar issue.
I was trying to manually update some plugin from node_modules, and when I reverted it back, i got this error.
I solved it by deleting node_modules and running NPM install.
In my case React was not in the scope of the file.
If you import a variable which has jsx from a different file which does not have react imported in it.
import React from "react";
Using following eslint plugin would avoid this: react-in-jsx-scope
Source: https://github.com/yannickcr/eslint-plugin-react/issues/84
In my case, an import was missing. Check your imports!
My solution is just deleting the cached images, files and cookies if you using the Chrome. Settings -> Privacy and security -> Clear browsing data -> Cached image and files / Cookies and other site data

Categories

Resources