Razzle: window is not defined

Created on 11 Feb 2020  路  14Comments  路  Source: jaredpalmer/razzle

Hi there,

I've run into the issue "window is not defined" when trying to run the 'server.js' file. The error is as bellow:

$ NODE_ENV=production node build/server.js
/Users/sanguyen/Documents/Work/my-app/node_modules/react-ace/lib/editorOptions.js:31
    if (window.ace) {
    ^

ReferenceError: window is not defined
    at Object.getAceInstance (/Users/sanguyen/Documents/Work/my-app/node_modules/react-ace/lib/editorOptions.js:31:5)
    at Object.<anonymous> (/Users/sanguyen/Documents/Work/my-app/node_modules/react-ace/lib/ace.js:31:27)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:22:18)
    at Object.<anonymous> (/Users/sanguyen/Documents/Work/my-app/node_modules/react-ace/lib/index.js:3:13)

When I'm trying to use "@elastic/eui" packge in my app.

Steps to reproduce:

  1. Create a new app
npx create-razzle-app my-app
  1. Installs required packages
cd my-app
yarn add @elastic/eui @elastic/datemath moment
  1. Open Home.js and replace the content as below
import React from 'react';
import logo from './react.svg';
import './Home.css';
import { EuiFieldPassword } from '@elastic/eui';

class Home extends React.Component {
  render() {
    return <div className="Home">
            <div className="Home-header">
                <img src={logo} className="Home-logo" alt="logo" />
                <h2>Welcome to Razzle</h2>
            </div>
            <p className="Home-intro">
                To get started, edit <code>src/App.js</code> or <code>src/Home.js</code> and save to reload.
            </p>
            <ul className="Home-resources">
                <li>
                    <a href="https://github.com/jaredpalmer/razzle">Docs</a>
                </li>
                <li>
                    <a href="https://github.com/jaredpalmer/razzle/issues">Issues</a>
                </li>
                <li>
                    <a href="https://palmer.chat">Community Slack</a>
                </li>
            </ul>
            <EuiFieldPassword placeholder="Placeholder text" value={'helllow there'} onChange={() => {}} aria-label="Use aria labels when no actual label is in use" />
        </div>;
  }
}

export default Home;

  1. Build
yarn build
  1. Start the server.js
yarn start:prod

Expected: will start successfully without issue
Actual: getting 'window is not defined'

I know the 'window' or 'document' object will not used or available in Node. So, is there any way that we can fix this issue?

Thanks

Most helpful comment

const ssrEuiFieldPassword =
  process.env.BUILD_TARGET === 'client' ? require('@elastic/eui').EuiFieldPassword : OtherComponent;

All 14 comments

You are rendering a rich text editor server side. This is a @elastic/eui or react-ace issue.

@fivethreeo got it. And Razzle supports SSR as default so it also means Razzle will not work if my app is using a rich text editor library?
BTW, since my app is currently using CRA with client side rendering and I'm thinking to convert to Razzle for SSR.

You could render something else server side.

@fivethreeo, Do you have any suggestion how to do that? Since, everything will be rendered under <App />.

As far as I know, whatever libraries/components which I used in the client side will be rendered in the server code.

` ReactDomServer.renderToString( <StaticRouter context={context} location={req.url}> <App /> </StaticRouter> );

const ssrEuiFieldPassword =
  typeof window !== 'undefined' ? EuiFieldPassword : OtherComponent;

@fivethreeo, It seems doesn't work, I added like this:

class Home extends React.Component {
    render() {
      const ssrEuiFieldPassword = typeof window !== 'undefined' ? EuiFieldPassword : div;
      return <div className="Home">
                <div className="Home-header">
                    <img src={logo} className="Home-logo" alt="logo" />
                    <h2>Welcome to Razzle</h2>
                </div>
                <p className="Home-intro">
                    To get started, edit <code>src/App.js</code> or <code>src/Home.js</code> and save to reload.
                </p>
                <ul className="Home-resources">
                    <li>
                        <a href="https://github.com/jaredpalmer/razzle">Docs</a>
                    </li>
                    <li>
                        <a href="https://github.com/jaredpalmer/razzle/issues">Issues</a>
                    </li>
                    <li>
                        <a href="https://palmer.chat">Community Slack</a>
                    </li>
                </ul>
                <ssrEuiFieldPassword placeholder="Placeholder text" value={'helllow there'} onChange={() => {}} aria-label="Use aria labels when no actual label is in use" />
            </div>;
    }
  }

  export default Home;

I could start the app successfully but I ran into the issue when accessing http://localhost:3000

$ NODE_ENV=production node build/server.js
馃殌 started
(node:8236) UnhandledPromiseRejectionWarning: ReferenceError: window is not defined
    at Object.getAceInstance (/Users/sanguyen/Documents/Work/my-app/node_modules/react-ace/lib/editorOptions.js:31:5)
    at Object.<anonymous> (/Users/sanguyen/Documents/Work/my-app/node_modules/react-ace/lib/ace.js:31:27)

I would do it outside the component so you don鈥檛 redefine it on each render/mount.

Try

const ssrEuiFieldPassword =
  process.env.BUILD_TARGET === 'client' ? EuiFieldPassword : OtherComponent;

Oh, misread that traceback. You need to do a conditional import/require.

const ssrEuiFieldPassword =
  process.env.BUILD_TARGET === 'client' ? require('@elastic/eui').EuiFieldPassword : OtherComponent;

Thanks @fivethreeo, it's working!

@samjetski ok if you did this you will get some hydration errors in browser console, according to ReadDOM docs:

If you intentionally need to render something different on the server and the client, you can do a two-pass rendering. Components that render something different on the client can read a state variable like this.state.isClient, which you can set to true in componentDidMount(). This way the initial render pass will render the same content as the server, avoiding mismatches, but an additional pass will happen synchronously right after hydration. Note that this approach will make your components slower because they have to render twice, so use it with caution.

so my suggestion here is to create a component called NoSSR like this:

class NoSSR extends React.Component {
  state = {
    isClient: false
  }
  componentDidMount() {
     this.setState({ isClient: true })
  }
  render() {
    const { isClient } = this.state
    const { children } = this.props
    return isClient ? children : null;
  }
}

and use it in this way:

<NoSSR>
  <EuiFieldPassword 
      placeholder="Placeholder text" 
      value={'helllow there'} 
      onChange={() => {}} 
      aria-label="Use aria labels when no actual label is in use" 
    />
</NoSSR>

GG EZ

Please Close this issue after you read this message.

Closed due to inactivity. Holler if this is a mistake, and we'll re-open it.

so my suggestion here is to create a component called NoSSR like this:

class NoSSR extends React.Component {
  state = {
    isClient: false
  }
  componentDidMount() {
     this.setState({ isClient: true })
  }
  render() {
    const { isClient } = this.state
    const { children } = this.props
    return isClient ? children : null;
  }
}

@nimaa77 I like your really nice solution for disabling SSR.

The solution is to disable SSR for your react-ace component. None of the examples given worked for me.

The below snippet is working in NextJS v10.0.3. Note that the use of require instead of import; your linter will probably flag this.

import React from "react";
import dynamic from "next/dynamic";

const Editor = dynamic(
    async () => {
        const ace = await require("react-ace");
        require("ace-builds/src-noconflict/mode-javascript");
        require("ace-builds/src-noconflict/theme-github");
        return ace;
    },
    {
        loading: () => (
            <p>Loading</p>
        ),
        ssr: false,
    },
);

export default function CodeEditor(props) {
    return (
        <Editor
            {...props}
        />
    );
}

...and then use it where you like just as you would have done with AceEditor:

<CodeEditor
    onChange={(text )=> console.log(text)}
    mode="javascript"
    theme="github"
    showPrintMargin={false}
    width="100%"
    editorProps={{ $blockScrolling: true }}
/>

I realise this repo isn't directly related to react-ace but this issue is currently a top result on Google for this error message. Hopefully this helps someone else out.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Jayphen picture Jayphen  路  4Comments

ewolfe picture ewolfe  路  4Comments

JacopKane picture JacopKane  路  3Comments

panbanda picture panbanda  路  5Comments

corydeppen picture corydeppen  路  3Comments