Skip to content

Is this SSR ready out of the box? #91

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

Closed
daydream05 opened this issue Jun 24, 2018 · 9 comments
Closed

Is this SSR ready out of the box? #91

daydream05 opened this issue Jun 24, 2018 · 9 comments

Comments

@daydream05
Copy link

I use Gatsby JS and was wondering if I need to do any modifications if I need this to work for SSR. Thanks

@theKashey
Copy link

react-media probably would not work with SSR, as long it does not provide methods to mock environment, and not match anything if you dont have window object (and you dont)

If you are looking for SSR-friendly media-matcher - take a look on https://github.com/thearnica/react-media-match

@edorivai
Copy link
Collaborator

We use this lib with SSR, I explained our workflow here: #50 (comment)

@edorivai
Copy link
Collaborator

@daydream05 I didn't read your original message properly. But getting this to work with Gatsby with your pre-rendered HTML will be impossible, since AFAIK Gatsby produces the HTML files at build time, at which point you have no information which device will be served.

I'm not familiar with Gatsby, but the only option I can think of is to render multiple versions of your site, but now you'll have to figure out how to serve the correct version to the correct user. You'll probably be better off with just picking a default (i.e. mobile), which will be rendered to your static HTML. And then let <Media> correct client-side if the default turns out to be a mismatch.

@theKashey
Copy link

The problem with React-media+SSR is ... the absence of documentation.

@edorivai
Copy link
Collaborator

We've just merged the PR with SSR docs!

@bradwestfall
Copy link

I was having this same issue on a Gatsby site I had planned on launching today. The problem with Gatsby SSR is that the static pages are created at build time so there's no opportunity to evaluate the client hitting the server to setup defaultMatches correctly.

I don't have much SSR experience and don't know exactly how componentWillMount works on SS and then how it gets called again client side, but here's what I was experiencing: <Media> wasn't returning the correct match when the page first loads, and would only work if I started resizing the window. In other words, <Media> will listen to the window changing size but only after the component loads. This was only happening in production since local Gatsby doesn't serve SSR, which is why I didn't notice the issue until today (site launch day).

Again, I'm no SSR expert and don't know how your componentWillMount works SS vs CS, but I do know that componentDidMount only works client side, so I came up with a nasty trick that I'm not proud of :(

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.state = { key: 0 }
  }

  componentDidMount() {
    this.setState({ key: 1 })
  }

  render() {
    return (
      <Media query="(max-width: 800px)" key={this.state.key}>
        {matches => {
          console.log(matches)
        }}
      </Media>
    )
  }
}

The premise of the trick (for those finding this and don't see it) is that componentDidMount only fires client side and not server side so by swapping out the key value as soon as my component loads on the client will force <Media> to re-evaluate the window size which means it doesn't matter how Gatsby or other SSR's give you the initial HTML, this will self-correct as soon as the page loads

However

This got me thinking about what if I changed <Media>'s code itself to use componentDidMount instead of componentWillMount. I copied the code for Media to be a local component on my project, changed Will to Did, published it into production and everything works. Since componentDidMount is client only, <Media> sets up its first evaluation client side, not SSR.

I went to fork and change and ran tests and I get one test that fails which I'm not sure how you want to solve. Here's the test line number

I think it fails because with componentDidMount, the state for matches is this.props.defaultMatches which is true by default, so since componentDidMount happens later than componentWillMount, a render gets called with this.state.matches being true before componentDidMount can set it to false.

In any case, this whole problem is fixed if we can change to componentDidMount. Sorry this is long

@edorivai
Copy link
Collaborator

Hi @bradwestfall 👋, thanks for your detailed write-up.

First of all, we are planning to move to componentDidMount already.

[I] ... don't know exactly how componentWillMount works on SS

componentWillMount does fire on the server. However, it will be deprecated in future versions, so relying on it would be unwise.

<Media> will listen to the window changing size but only after the component loads

I think I figured this one out. Since componentWillMount is called before render, calling setState here will not trigger a rerender. Now since setState is not actually synchronous - you're queuing a state change - the change might only be reflected after the render has completed. Normally this is no problem, as the state change would subsequently trigger a rerender, but it doesn't from componentWillMount.

I guess this problem is one of the motivations of deprecating componentWillMount, and it's another reason for use to move to componentDidMount 😄

@bradwestfall
Copy link

Thanks for the fast response. Any idea on a timeframe?

@edorivai
Copy link
Collaborator

Sorry, can't be sure about a timeline. @mjackson is a busy man, and I'd like to pass these changes by him before publishing.

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

No branches or pull requests

4 participants