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

Prevent SSR for Component #292

Closed
nym opened this issue Jun 28, 2016 · 6 comments
Closed

Prevent SSR for Component #292

nym opened this issue Jun 28, 2016 · 6 comments
Labels

Comments

@nym
Copy link

nym commented Jun 28, 2016

I have a problem almost identical to erikras/react-redux-universal-hot-example#507

Is there a way to disable server side rendering for a specific component, or add it only when the renderer is in the browser?

@choonkending
Copy link
Member

choonkending commented Jun 30, 2016

I haven't tested this myself, but what about trying to triggering a state in the componentDidMount lifecycle method?

Lifecycle

constructor(props) {
    super(props);
    this.state = { shouldRenderComponent: false };
}

componentDidMount() {
   this.setState({ shouldRenderComponent: true });    
}

render() {
    return (<div>{ this.state.shouldRenderComponent && <Component> }</div>);
}

or

Just check in render

render() {
    const shouldRender = typeof window !== 'undefined'; // 
    return (<div>{ shoulderRender && <Component }</div>);
}

or

Wrap it in a HOC

const onlyRenderInBrowser = ComposedComponent => class extends React.Component {
    render() {
         const shouldRender = some browser check;
         if (shouldRender) return <ComposedComponent {...this.props} />;
         return null;
    }
}

... Your component then remains pure

export default onlyRenderInBrowser(SomeComponent);

(I haven't tried any of the above, but that's how I would go about solving it). Hopefully that helps.

@nym
Copy link
Author

nym commented Jul 2, 2016

Here's what I'm using right now:

export default class MapView extends Component {
  constructor () {
    super()
  }

  componentDidMount () {
  }

  render () {
    const { position, zoom, bounds } = this.props;

    if (process.title == "browser") {
      var {Map, Marker, Popup, TileLayer} = require('react-leaflet');
      return (
        <div className="search-map">
          <Map bounds={bounds} center={position} zoom={zoom} style={{height: 300}} dragging={false} scrollWheelZoom={false}>
            <TileLayer
              url='https://api.mapbox.com/styles/v1/blockfight/ciq1nllez0043bfm5lcg4i75o/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1IjoiYmxvY2tmaWdodCIsImEiOiJjaXAwM3kxdWIwMmVxdHdtOGxvdHl4aDF5In0.dYdOkCrDlHgKJoEKdgFjFw'
              attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
              />
          </Map>
        </div>
      );
    }
    else {
      return null;
    }
  }
}

Key problem is that the checksum fails since the server side is empty where the client side has the leaflet map.

Ideally the markup generated by the server is reused, we need to do it anyway for SEO, but not every client side component has to be included, e.g. a graph or input.

@nym
Copy link
Author

nym commented Jul 2, 2016

Here's the warning I routinely get:

warning.js:44 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) react-empty: 35 --><div class="search-ma
 (server) react-empty: 35 --><section data-reactid

@psimyn
Copy link
Collaborator

psimyn commented Jul 10, 2016

@nym @choonkending's example should resolve your error - since content will be the same on client and server, and the component will update only on client. For your example:

export default class MapView extends Component {
  constructor () {
    super()
    this.state = {
      shouldRender: false
    }
  }

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

  render () {
    const { position, zoom, bounds } = this.props;

    if (this.state.shouldRender) {
      var {Map, Marker, Popup, TileLayer} = require('react-leaflet');
      return (
        <div className="search-map">
          <Map bounds={bounds} center={position} zoom={zoom} style={{height: 300}} dragging={false} scrollWheelZoom={false}>
            <TileLayer
              url='https://api.mapbox.com/styles/v1/blockfight/ciq1nllez0043bfm5lcg4i75o/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1IjoiYmxvY2tmaWdodCIsImEiOiJjaXAwM3kxdWIwMmVxdHdtOGxvdHl4aDF5In0.dYdOkCrDlHgKJoEKdgFjFw'
              attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
              />
          </Map>
        </div>
      );
    }
    else {
      return null;
    }
  }
}

So server renders original markup without MapView
Client checks the component, does render-diffing, without MapView
ComponentDidMount triggers a setState, which triggers re-render. This time, we render MapView

@slavab89
Copy link
Member

We're cleaning up issues on the project and since this is an old issue we're closing it.
If you still experience this or have an issue, please comment here or open a new one

@nym
Copy link
Author

nym commented Oct 17, 2017

I haven't been using it recently, so fine with me.

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

No branches or pull requests

4 participants