Is this SSR ready out of the box?
daydream05 opened this issue · comments
I use Gatsby JS and was wondering if I need to do any modifications if I need this to work for SSR. Thanks
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
We use this lib with SSR, I explained our workflow here: #50 (comment)
@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.
The problem with React-media+SSR is ... the absence of documentation.
We've just merged the PR with SSR docs!
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
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
😄
Thanks for the fast response. Any idea on a timeframe?