Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

window is not defined #1193

Closed
sangnguyen7 opened this issue Feb 11, 2020 · 14 comments
Closed

window is not defined #1193

sangnguyen7 opened this issue Feb 11, 2020 · 14 comments

Comments

@sangnguyen7
Copy link

sangnguyen7 commented Feb 11, 2020

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

@fivethreeo
Copy link
Collaborator

fivethreeo commented Feb 11, 2020

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

@sangnguyen7
Copy link
Author

@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.

@fivethreeo
Copy link
Collaborator

You could render something else server side.

@sangnguyen7
Copy link
Author

sangnguyen7 commented Feb 11, 2020

@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>
);
``

@fivethreeo
Copy link
Collaborator

fivethreeo commented Feb 11, 2020

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

@sangnguyen7
Copy link
Author

sangnguyen7 commented Feb 11, 2020

@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)

@fivethreeo
Copy link
Collaborator

fivethreeo commented Feb 12, 2020

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

Try

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

@fivethreeo
Copy link
Collaborator

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

@fivethreeo
Copy link
Collaborator

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

@sangnguyen7
Copy link
Author

Thanks @fivethreeo, it's working!

@nimaa77
Copy link
Collaborator

nimaa77 commented Mar 16, 2020

@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.

@nimaa77
Copy link
Collaborator

nimaa77 commented Mar 17, 2020

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

@nimaa77 nimaa77 closed this as completed Mar 17, 2020
@ksuess
Copy link

ksuess commented Dec 16, 2020

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.

@marknorrapscm
Copy link

marknorrapscm commented Jan 5, 2021

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants