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

Render only in client side #863

Closed
NourSammour opened this issue Jan 24, 2016 · 16 comments
Closed

Render only in client side #863

NourSammour opened this issue Jan 24, 2016 · 16 comments

Comments

@NourSammour
Copy link

is it possible to trigger render only in client side, not on the server
i'm trying to use this example (google map)
http://tomchentw.github.io/react-google-maps/async/

Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component.

my goal is to load the google map library and then render the map on the client side only

Any help?

@NourSammour NourSammour changed the title render only in client side Render only in client side Jan 24, 2016
@micooz
Copy link
Contributor

micooz commented Jan 24, 2016

How about partial render on client side?

componentDidMount() {
    if (__CLIENT__) {
        // initialize google map here
    }
}

@NourSammour
Copy link
Author

@micooz i tried very similar to your answer but get warning

import {default as canUseDOM} from 'can-use-dom';
render() {
    if (!canUseDom) {
      return (<div/>);
    }
    return (
      <ScriptjsLoader ... )
}

but i get warning

Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:

@quicksnap
Copy link
Collaborator

I would recommend using __CLIENT__. Best to try and avoid those switches when possible, and instead use something like componentDidMount

@NourSammour
Copy link
Author

@quicksnap i get this waring when i used __CLIENT__ with componentDidMount

setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component.

@Dattaya
Copy link

Dattaya commented Jan 25, 2016

@NourSammour I don't think you need __CLIENT__ inside componentDidMount 'cause componentDidMount is only called on the client. Try to do it like so:

...
  state = {
    scriptjsLoaderEnabled: false
  };

  componentDidMount() {
    // initialize the lib here?
    // ...
    this.setState({scriptjsLoaderEnabled: true})    
  }

  render() {
    if (!this.state.scriptjsLoaderEnabled) {
      return (<div/>);
    }
    return (
      <ScriptjsLoader ... )
  }

@NourSammour
Copy link
Author

@Dattaya Thanks, it's work perfectly 👍
the issue solved

@OkuraSt
Copy link

OkuraSt commented Feb 16, 2016

Hi, im trying to get this working, I have the same problem described above. Ive tried the solution with componentDidMount but the error persist. Maybe Im missing something.
Ive made a repository with the initial boilerplate and only adding the map component called AsyncMap:

https://github.com/OkuraSt/example-maps

The Map component is added on Home.js.
Could someone give me a hint, Thanks for your time.

@NourSammour
Copy link
Author

@OkuraSt try this

export default class MyMap extends Component {
  state = {
    inBrowser: false
  };
  componentDidMount() {
    this.setState({ inBrowser: true });
  }
  render() {
    if (this.state.inBrowser) {
        res = this.renderMap();
    } else {
      res = <span>Loading</span>;
    }
    return res;
  }
}

@OkuraSt
Copy link

OkuraSt commented Feb 16, 2016

@NourSammour Thanks man,
Ive tried your suggestion, my component now looks like this:

export default class AsyncMap extends Component {

  state = {
    markers: [{
      position: {
        lat: 25.0112183,
        lng: 121.52067570000001,
      },
      key: 'Taiwan',
      defaultAnimation: 2
    }],
    inBrowser: false
  }

  componentDidMount() {
    // initialize the lib here?
    // ...
    this.setState({inBrowser: true});
  }

  renderNewBehavior() {...}

  render() {
    let res = null;
    if (this.state.inBrowser) {
      res = this.renderNewBehavior();
    } else {
      res = <span>Loading</span>;
    }
    return res;
  }
}

image

I don't know if I'm missing something very easy, but the error is persisting.
I really appreciate your time, thanks

@OkuraSt
Copy link

OkuraSt commented Feb 22, 2016

I could get it working by setting the height and width of the container element to a specific number of pixels. Now Im tryng to resize it to 100% height. I appreciate any help. Thanks

@Znack
Copy link

Znack commented Feb 28, 2016

Hey, guys, @OkuraSt, @NourSammour, @Dattaya! Does anybody see the problem when you use __CLIENT__ or componentDidMount for detecting client-side and rendering specific code the console show error about difference in server-side and client-side markup?
For example:

Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
 (client) hvf5a2k1s.2.0.1.0"><div class="form-row 
 (server) hvf5a2k1s.2.0.1.0"></div></div><div data

As you can see, on the server side my component is simple </div></div> however on the client side it has real content starting with <div class="form-row.

Does this case make the React to re-render the whole DOM-tree and all server side rendering become a unuseful stuff or it just tries to re-render only this specific component?

Thanks a lot.

@Dattaya
Copy link

Dattaya commented Feb 28, 2016

Hi, @Znack. This shouldn't be the case, unless you use __CLIENT__ inside the render method. Initially the server and the client return the same virtual dom, then when component has been mounted on the client side, in componentDidMount we set a new state and of course it rerenders the component.

@Znack
Copy link

Znack commented Feb 28, 2016

@Dattaya, yep, actually I used __CLIENT__ inside the render method, so when I implemented your suggestion with setting new state in componentDidMount everything become working. Thank you very much.
But I have met one eslint issue: react/no-did-mount-set-state rule does not allow call setState within componentDidMount, I suppose it is some kind of a bad practice, but I have disabled eslint for this particular line. I hope it will not be a cause of some unpredicted strange problems in the future ;)

@gabriel-miranda
Copy link

@Dattaya You can make another function and eslint won't trigger

componentDidMount() {
  setClientSideState();
}
setClientSideState() {
  this.setState({client: true});
}

@Dattaya
Copy link

Dattaya commented Feb 28, 2016

Thank you, @gabriel-miranda, good to know.
@Znack, here's some reasoning behind that rule (no-did-mount-set-state)--https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md; not that I completely understand it, but for this particular case I think it's legit to use setState.

@Znack
Copy link

Znack commented Feb 29, 2016

Thank you, @gabriel-miranda, @Dattaya, I also suppose it is not the big deal to use setState for this particular case, so I have used comment eslint-disable-line and now everything is great.

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

7 participants