- How to solve «window is not defined» errors in React and Next.js
- 1. First solution: typeof
- 2. Second solution: the useEffect hook
- 3. Third solution: dynamic loading
- Зачем в функцию передаются следующие аргументы: window, document, undefined?
- What function window, document, undefined — window, document really means
- Scope
- How it works
- Free eBook
- Arguments
- What about undefined ?
- Minifying
- Non-browser global environments
- Learn JavaScript the right way.
How to solve «window is not defined» errors in React and Next.js
Next.js is a React framework with pre-rendering abilities. This means that for every page, Next.js will try to generate the HTML of the page for better SEO and performance. This is why, if you’re trying to do this:
// components/Scroll.js window.addEventListener("scroll", function() console.log("scroll!") >);
Then it will fail with «ReferenceError: window is not defined»: Because in the Node.js world, window is not defined, window is only available in browsers. There are three ways to solve that:
1. First solution: typeof
if (window !== undefined) // browser code >
Because this would try to compare a non-existent variable (window) to undefined, resulting in the mighty «ReferenceError: window is not defined». You can still use:
if (typeof window !== "undefined") // browser code >
Because typeof won’t try to evaluate «window», it will only try to get its type, in our case in Node.js: «undefined». PS: Thanks to
Rogier Nitschelm for reminding me about this. I initially tried to do if (typeof window !== undefined) and this failed hard because of the reasons mentioned earlier. The other solutions below are more exotic but still worth it.
2. Second solution: the useEffect hook
The «React» way to solve this issue would be to use the useEffect React hook. Which only runs at the rendering phase, so it won’t run on the server. Let’s update our scroll.js component:
// components/Scroll.js import React, useEffect > from "react"; export default function Scroll() useEffect(function mount() function onScroll() console.log("scroll!"); > window.addEventListener("scroll", onScroll); return function unMount() window.removeEventListener("scroll", onScroll); >; >); return null; >
What we’ve done here is to turn our initial JavaScript file into a true React component that then needs to be added to your React tree via:
// pages/index.js import Scroll from "../components/Scroll"; export default function Home() return ( div style= <minHeight: "1000px" >>> h1>Home/h1> Scroll /> /div> ); >
Tip: The way we use useEffect in the example is to register and unregister the listeners on mount/unmount. But you could also just register on mount and ignore any other rendering event, to do so you would do this:
// components/Scroll.js import React, useEffect > from "react"; export default function Scroll() useEffect(function onFirstMount() function onScroll() console.log("scroll!"); > window.addEventListener("scroll", onScroll); >, []); // empty dependencies array means "run this once on first mount" return null; >
3. Third solution: dynamic loading
A different solution is to load your Scroll component using dynamic imports and the srr: false option. This way your component won’t even be rendered on the server-side at all. This solution works particularly well when you’re importing external modules depending on window . (Thanks Justin!)
// components/Scroll.js function onScroll() console.log("scroll!"); > window.addEventListener("scroll", onScroll); export default function Scroll() return null; >
// pages/index.js import dynamic from "next/dynamic"; const Scroll = dynamic( () => return import("../components/Scroll"); >, ssr: false > ); export default function Home() return ( div style= <minHeight: "1000px" >>> h1>Home/h1> Scroll /> /div> ); >
If you do not need the features of useEffect, you can even remove its usage completely as shown here. Finally, you could also load your Scroll component only in _app.js if what you’re trying to achieve is to globally load a component and forget about it (no more mount/unmount on page change). I have used this technique to display a top level progress bar with NProgress in this article:
Зачем в функцию передаются следующие аргументы: window, document, undefined?
Объясните пожалуйста или дайте ссылку где можно почитать на русском языке о том, зачем в функцию передаются следующие аргументы: window, document, undefined.
(function(window, document, undefined) < // что-нибудь происходит и возвращается значение >)(window, document);
Оценить 2 комментария
1) Код оборачивается в функцию, чтобы изолировать область видимости переменных
2) переменные window, document, undefined передаются в функцию, чтобы она лучше минифицировалась. После прогона минификатором этот код превратится в
(function(w, d, u) < // что-нибудь происходит и возвращается значение >)(window, document);
и все внутренние обращения к window, document и undefined будут заменены на обращение к w, d и u.
@Yavanosta тоесть надо полагать, что никакого скрытого смысла в передаче этих переменных нет. Делается это просто для лучшей минификации кода? Но тогда зачем переменная undefined?
Да, для лучшей минификации. Переменная undefined (её значение будет как раз таки undefined, т.к. третий параметр в функцию не передается) также нужна для лучшей минификации. Если вам надо проверить является ли переменная undefined, вы можете либо написать typeof(myvar) = «undefined» и такой код не будет минифицирован, либо сравнить с любой другой, гарантированно undefined переменной.
Соответственно имея переменную undefined специально созданную для этих целей писать myvar == undefined. Такой код будет минифицирован в myvar == u. Ну и плюс лично мне проверка в стиле myvar == undefined больше нравится чем typeof(myvar) comment__date»> Написано более трёх лет назад
What function window, document, undefined — window, document really means
The most complete guide to learning JavaScript ever built.
Trusted by 82,951 students .
In this post, we’re going to explore what the title suggests, and offer explanations as to what this self invoked function setup gives us.
Interestingly enough I get asked about the IIFE (immediately-invoked function expression) a lot, which takes the following setup:
(function (window, document, undefined) // >)(window, document);
So why not write a post about it? 😉
First, this does a series of different things. From the top:
Scope
JavaScript has function scope, so first this creates some much needed “private scope”. For example:
(function (window, document, undefined) var name = 'Todd'; >)(window, document); console.log(name); // name is not defined, it's in a different scope
How it works
A normal function looks like this:
var logMyName = function (name) console.log(name); >; logMyName('Todd');
We get to invoke it by choice, and wherever we want/can scope providing.
The reason “IIFE” was coined was because they’re immediately-invoked function expressions. Which means they’re immediately called at runtime — also we can’t call them again they run just once like this:
var logMyName = (function (name) console.log(name); // Todd >)('Todd');
The secret sauce here is this, (which I’ve assigned to a variable in the previous example):
The extra pair of parentheses is necessary as this doesn’t work:
Free eBook
Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.
- Observables and Async Pipe
- Identity Checking and Performance
- Web Components syntax
- and Observable Composition
- Advanced Rendering Patterns
- Setters and Getters for Styles and Class Bindings
That went smoothly, check your email.
Though several tricks can be done to trick JavaScript into “making it work”. These force the JavaScript parser to treat the code following the ! character as an expression:
There are also other variants:
+function () >(); -function () >(); ~function () >();
Check out Disassembling JavaScript’s IIFE Syntax by @mariusschulz for a detailed explanation of the IIFE syntax and its variants.
Arguments
Now we know how it works, we can pass in arguments to our IIFE:
How does this work? Remember, the closing (window); is where the function is invoked, and we’re passing in the window Object. This then gets passed into the function, which I’ve named window also. You could argue this is pointless as we should name it something different — but for now we’ll use window as well.
So what else can we do? Pass in all the things! Let’s pass in the document Object:
(function (window, document) // we refer to window and document normally >)(window, document);
Local variables are faster to resolve than the global variables, but this is on a huge scale and you’ll never notice the speed increase — but also worth considering if we’re referencing our globals a lot!
What about undefined ?
In ECMAScript 3, undefined is mutable. Which means its value could be reassigned, something like undefined = true; for instance, oh my! Thankfully in ECMAScript 5 strict mode ( ‘use strict’; ) the parser will throw an error telling you you’re an idiot. Before this, we started protecting our IIFE’s by doing this:
(function (window, document, undefined) >)(window, document);
Which means if someone came along and did this, we’d be okay:
undefined = true; (function (window, document, undefined) // undefined is a local undefined variable >)(window, document);
Minifying
Minifying your local variables is where the IIFE pattern’s awesomeness really kicks in. Local variable names aren’t really needed if they’re passed in, so we can call them what we like.
(function (window, document, undefined) console.log(window); // Object window >)(window, document);
(function (a, b, c) console.log(a); // Object window >)(window, document);
Imagine it, all your references to libraries and window and document nicely minified. Of course you don’t need to stop there, we can pass in jQuery too or whatever is available in the lexical scope:
(function ($, window, document, undefined) // use $ to refer to jQuery // $(document).addClass('test'); >)(jQuery, window, document); (function (a, b, c, d) // becomes // a(c).addClass('test'); >)(jQuery, window, document);
This also means you don’t need to call jQuery.noConflict(); or anything as $ is assigned locally to the module. Learning how scopes and global/local variables work will help you even further.
A good minifier will make sure to rename undefined to c (for example, and only if used) throughout your script too. Important to note, the name undefined is irrelevant. We just need to know that the referencing Object is undefined, as undefined has no special meaning — undefined is the value javascript gives to things that are declared but have no value.
Non-browser global environments
Due to things such as Node.js, the browser isn’t always the global Object which can be a pain if you’re trying to create IIFE’s that work across multiple environments. For this reason, I tend to stick with this as a base:
In a browser, the global environment this refers to the window Object, so we don’t need to pass in window at all, we could always shorten it to this .
I prefer the name root as it can refer to non-browser environments as well as the root of the browser.
If you’re interested in a universal solution (which I use all the time nowadays when creating open source project modules) is the UMD wrapper:
(function (root, factory) if (typeof define === 'function' && define.amd) define(factory); > else if (typeof exports === 'object') module.exports = factory; > else root.MYMODULE = factory(); > >)(this, function () // >);
This is some sexy stuff. The function is being invoked with another function passed into it. We can then assign it to the relevant environment inside. In the browser, root.MYMODULE = factory(); is our IIFE module, elsewhere (such as Node.js) it’ll use module.exports or requireJS if typeof define === ‘function’ && define.amd resolves true.
But this stuff is another story, but I insist you check out the UMD repo.
Learn JavaScript the right way.
The most complete guide to learning JavaScript ever built.
Trusted by 82,951 students .