react-d3-tree icon indicating copy to clipboard operation
react-d3-tree copied to clipboard

ReferenceError: window is not defined - Server-Side Rendering (SSR)

Open KrzysztofKarol opened this issue 8 years ago • 15 comments

How to use this great library properly with Server-Side Rendering (SSR)

ReferenceError: window is not defined

- react-d3-tree.min.js:1 
    [frontend]/[react-d3-tree]/lib/react-d3-tree.min.js:1:4937
  
  - react-d3-tree.min.js:1 
    [frontend]/[react-d3-tree]/lib/react-d3-tree.min.js:1:4899
  
  - react-d3-tree.min.js:1 e.exports
    [frontend]/[react-d3-tree]/lib/react-d3-tree.min.js:1:5409

return window&&document&&document.all&&!window.atob

KrzysztofKarol avatar Oct 17 '17 09:10 KrzysztofKarol

Hi @KrzysztofKarol, thank you for reporting this! I was actually unaware of how/if this library worked with SSR. The source doesn't make any reference to document/window, so seems to be a bundling/dependency issue, will look into this after work today.

As a workaround until this is implemented you could maybe render the tree on componentDidMount to ensure it renders in the client?

bkrem avatar Oct 17 '17 10:10 bkrem

Hi @bkrem Thank you for your almost instantaneous answer. Yes, I checked that too. I built it without UglifyJsPlugin and it's used for

isOldIE = memoize(function() {
	// Test for IE <= 9 as proposed by Browserhacks
	// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
	// Tests for existence of standard globals is to allow style-loader
	// to operate correctly into non-standard environments
	// @see https://github.com/webpack-contrib/style-loader/issues/177
	return window && document && document.all && !window.atob;
}),

I investigated problem a little https://github.com/webpack-contrib/style-loader/blob/67120f8dc831626f466d64513b4e26074e89341b/lib/addStyles.js#L23 Problem occures during import

// react-d3-tree.js
module.exports = function(list, options) {
  // Here DEBUG is not defined
  if (typeof DEBUG !== "undefined" && DEBUG) {
    if (typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
  }

  options = options || {};

  options.attrs = typeof options.attrs === "object" ? options.attrs : {};

        // Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
        // tags it will allow on a page
  if (!options.singleton) options.singleton = isOldIE();

KrzysztofKarol avatar Oct 17 '17 11:10 KrzysztofKarol

Ahh interesting, thanks for digging into this already.

Seems like the least path of resistance would be to set options.singleton if it's part of the style-loader public API since IE 8 and older isn't supported by the React team anymore anyways. Otherwise it would be worth working around the style-loader plugin if that gets SSR working.

If you have an idea feel free to put in a PR @KrzysztofKarol, otherwise I'll check this out in detail this evening :)

bkrem avatar Oct 17 '17 13:10 bkrem

I couldn't find simple and good solution. So i made it very dirty way for now - I used isomorphic-style-loader instead of style-loader. https://github.com/KrzysztofKarol/react-d3-tree/commit/f876afec64f5fbc1932f950c481a2b8ae9d032bc

BTW. I also encountered a problem with react-transition-group. react-d3-tree uses 1.1.3 as external and it's somehow overriding 2.2.1 used by material-ui@next.

KrzysztofKarol avatar Oct 17 '17 14:10 KrzysztofKarol

Ok cool, good to hear you found a way. If isomorphic-style-loader seems to do the job I'll move the build pipeline to that.

Ok that's a strange one regarding react-transition-group, might make sense to bundle the dependencies directly rather than using externals in that case.

Thanks for reporting these two already, super useful! 🙌

bkrem avatar Oct 17 '17 14:10 bkrem

@bkrem @KrzysztofKarol Is this issue fixed? Somehow I'm still seeing the same error during server side rendering.

amazing work by the way, looking forward to using it!

codemonkeycxy avatar May 29 '18 16:05 codemonkeycxy

Hi @codemonkeycxy!

I wasn't using this lib for months so you have to ask @bkrem directly.

KrzysztofKarol avatar May 29 '18 16:05 KrzysztofKarol

Hi @codemonkeycxy,

SSR still isn't technically supported. I spent an entire day at the time tinkering with the isomorphic loader that Krzysztof suggested (and one or two other ones).

I couldn't get a stable version out of it and there were more pressing issues to resolve with the library so I closed this as out of scope (sorry for my lack of explanation when I closed it), since this error is preventable if you await componentDidMount() like so:

class MyTreeComponent extends React.PureComponent {
  state = {
	didMount: false
  }

  componentDidMount() {
	this.setState({
	  didMount: true
	})
  }

  render() {
	<div>{this.state.didMount && <Tree />}</div>
  }
}

I'm aware that this isn't optimal (setState in componentDidMount will cause an extra render call) and adds some boilerplate, but I simply haven't had the time to get the SSR issue resolved recently.

I hope this helps as a workaround for now and I will obviously update the issue once a fix is in place!

bkrem avatar May 30 '18 11:05 bkrem

@codemonkeycxy you can also use npm's package react-no-ssr as a workaround

https://github.com/kadirahq/react-no-ssr/blob/master/src/index.js

KrzysztofKarol avatar May 30 '18 11:05 KrzysztofKarol

@KrzysztofKarol How is this meant to be used?

I see it's a component on NPM now, but I'm still getting window is not defined when used like:

const Root = () => (
  <Provider store={store}>
    <NoSSR>
      <ComponentThatUsesWindow />
    </NoSSR>
  </Provider>
)

thomashibbard avatar Jul 10 '18 14:07 thomashibbard

@thomashibbard I think yes but this repo has latest commit dated on 9 Apr 2016 so I don't know how does it work with latest React versions. You can report an issue https://github.com/kadirahq/react-no-ssr/issues but I think that no one is maintaining this lib anymore, sadly.

KrzysztofKarol avatar Jul 10 '18 21:07 KrzysztofKarol

@thomashibbard same window not defined issue even with NoSSR. I finally got it working by doing a inline import

const Tree = require('react-d3-tree').Tree;
return (
  <div style={{width: '100vw', height: '100vh'}}>
    <Tree data={data} onClick={this.onClick} />
  </div>
);

codemonkeycxy avatar Jul 15 '18 04:07 codemonkeycxy

let Tree = ' '
class MyComponent extends React.PureComponent {
	state = {
		redender: false
	};
	async componentDidMount() {
		let res = await import("react-d3-tree");
		Tree = res.Tree;
		this.setState({ redender: true, window });
	}
	render() {
		return <div style={{ height: 1000, width: 1000 }}>{this.state.redender && <Tree data={myTreeData} />}</div>;
	}
}

this worked for me

HACHIMIam avatar Feb 26 '19 20:02 HACHIMIam

If anyone is still strugling ?

For Imports if (canUseDOM()) { const ReactD3Tree = require('react-d3-tree'); Tree = ReactD3Tree.default; }

For Usage if (!canUseDOM()) return null; return (

);

Function (canUseDOM) export function canUseDOM() { return !!(typeof window !== 'undefined' && window.document && window.document.createElement); }

Zunaib avatar Oct 16 '19 11:10 Zunaib

Any update with integration of this to Gatsby?

gerald-menong avatar Oct 05 '20 00:10 gerald-menong