bkrem / react-d3-tree

:deciduous_tree: React component to create interactive D3 tree graphs
https://bkrem.github.io/react-d3-tree
MIT License
1.08k stars 270 forks source link

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

Open KrzysztofKarol opened 7 years ago

KrzysztofKarol commented 7 years ago

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

bkrem commented 7 years ago

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?

KrzysztofKarol commented 7 years ago

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();
bkrem commented 7 years ago

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

KrzysztofKarol commented 7 years ago

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.

bkrem commented 7 years ago

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! 🙌

codemonkeycxy commented 6 years ago

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

KrzysztofKarol commented 6 years ago

Hi @codemonkeycxy!

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

bkrem commented 6 years ago

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!

KrzysztofKarol commented 6 years ago

@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

thomashibbard commented 6 years ago

@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>
)
KrzysztofKarol commented 6 years ago

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

codemonkeycxy commented 6 years ago

@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>
);
HACHIMIam commented 5 years ago
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

Zunaib commented 5 years ago

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 ( <div className="react-tree" id="treeWrapper" style={{ width: '150em', height: '150em' }}> <Tree {...props} />

);

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

gerald-menong commented 4 years ago

Any update with integration of this to Gatsby?