-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Component is partially re-rendering on client #12413
Comments
Hey! Thanks for this question! This is how our SSR at build-time works. That logic will be parsed once when the component renders (in a Node.js environment) and then will be rendered to HTML. Once the client-side code picks up (e.g. when run in a browser) that logic won't be re-evaluated because there's nothing that will trigger re-evaulating that code. If you want some type of re-evaluation, you'd need to do a dynamic, stateful check, something like the below should work: import React, { Component } from 'react'
export default class ClientServer extends Component {
state = {
env: 'server'
}
componentDidMount() {
this.setState({
env: `client`
})
}
render() {
const { env } = this.state
return (
<React.Fragment>
{/* This will render (As static HTML) I am rendered on the server, and will update client-side with I am rendered on the client */}
<h1>I am rendered on the {env}</h1>
</React.Fragment>
)
}
} Hopefully I've adequately explained this. Is there anything else I can help with? |
Got it, I was hoping there is a way to avoid it. Do you think it deserves a mention in the documentation? Thank you very much and keep up the good work! |
Not sure. I don't think we'd necessarily want to encourage this pattern. We'd moreso recommend to write your code in a way that doesn't necessarily have to care whether rendered on the client or server. If you want access to window globals, treat those as enhancements on top of the initial base--not necessarily serving differing behavior or content which doesn't make a ton of sense. Could you describe your use-case in more detail? Maybe I'm missing something! |
Hey, sorry for the delayed response. In the application I'm currently working on, we have a very complicated layout which changes based on window size (breakpoint) and scroll position. Usually we solve this by rendering two components and hide one on small breakpoints and other on large, using CSS. In this case it is a page layout, and we would have to render the whole page twice. Breakpoint and scroll position values are both It is a niche usecase. |
Please mention this in the documentation. I've spent too much time finding this info. In my use case, I use localstorage to persist user's selection on previous page (or in previous session) and change the className based on that. Pretty similar to how Author's original component. Maybe a quick mention here: https://www.gatsbyjs.org/docs/debugging-html-builds/ ? It's confusing that the behaviour is different in develop mode vs in html build. |
I agree, even if it is gatsby anti-pattern, it should be mentioned. For now, (components that are not important for SEO) we are not rendering on server at all, by using this high order component: import React, { Component } from 'react';
/*
Unfortunately gatsby assumes server and first client render are identical.
Therefore it doesn't rerender components on client side even their props have changed.
Check this issue for more information:
https://github.com/gatsbyjs/gatsby/issues/12413#issuecomment-470987990
*/
const renderOnClient = ComposedComponent => class RenderOnClient extends Component {
state = {
isClient: false,
}
componentDidMount() {
this.setState({
isClient: true,
});
}
render() {
const {
isClient,
} = this.state;
return isClient ? <ComposedComponent { ...this.props } /> : null;
}
};
export default renderOnClient; |
…cified component on the client. This resolves a partial re-rendering issue caused by Gatsby's Server Side Rendering: gatsbyjs/gatsby#12413 The makeVolatile() wrapper is generalized such that it can be applied to any component that may not be identical between SSR and first client side render.
* Add reception location * Randomize featured speakers * Add a Volatile HOC wrapper that will force full re-rendering of a specified component on the client. This resolves a partial re-rendering issue caused by Gatsby's Server Side Rendering: gatsbyjs/gatsby#12413 The makeVolatile() wrapper is generalized such that it can be applied to any component that may not be identical between SSR and first client side render.
This should be mentioned in documentation. I've also experienced some strange behaviour where a component partially updates its HTML output on hydration. |
Yes please on the documentation! I just wasted almost 3 working days puzzling over this. What I had was a youtube component that I only wanted to render if I know the user has accepted cookies (oh the joys of GDPR), and to render an alert instead if they haven't. For the life of me I couldn't work out why, when building and deploying the production site, once cookies were accepted, if I reloaded the page the component rendered the Youtube embed INSIDE the alert, like it was some sort of Schrodinger's Youtube Component, in two states at once. I was tearing my hair out :) Well it appears it's because during SSR it draws the alert, then on hydration it only updates what it "knows" has changed and leaves the "outer" alert intact, causing this visual glitch. Thanks to the info in here and in a stackoverflow thread I eventually figured out that I need to build state into the component and trigger a state change via componentDidMount to force it to do a full re-render on hydration. My implementation is a bit convoluted but I use connect(mapStateToProps) from redux to update the component's props, and then componentDidMount and componentDidUpdate (with "if" statement to make sure it's actually changed, to avoid an infinite loop error) to feed this prop back into the component's local state which causes it to re-render. So my "acceptCookies" variable gets passed around a lot... it goes from redux context to a prop and then to component state. But it works at long last. |
Agreed about adding this to the documentation. In my case, I needed to use dangerouslySetInnerHTML which behaves slightly differently in CSR and SSR (server side, it wraps the contents of the __html property in an extra div), so the server render and client render weren't identical, causing an odd behavior where gatsby was rendering the resulting component partly in English (the default language that's rendered during SSR) and partly in the intended localized language. I think it was reusing the server-side div (on which the property dangerouslySetInnerHTML was set) and since that property was originally the english text (on the server side), it was never updating. |
@codebravotech I believe we're hitting a similar issue - how'd you address it? We've got something akin to: const Content = ({html}) => <div dangerouslySetInnerHTML={{ __html: html }} />; In a production build on initial load it reads in some state/props, sets that html, and wont always update it correctly. If i use the 'html' fragment in anything else (e.g. Seems very related to the above, but a sightly different case. |
@dcramer Yep, so since the server-side tag is getting reused (not the contents, just the tag), the prop dangerouslySetInnerHtml (which is part of the tag) is not getting the updated value of the new props. I'm not totally clear on what is causing that behavior in ReactSSR and gatsby, but the good news is it's an easy fix. (The approach is outlined in some of the comments above- friendly reminder to always make sure you really understand the whole comment chain before asking the same question again. If you have questions or don't understand, its always cool to clarify, but it's helpful that others know you've familiarized yourself with all the information on the chain.) So- Make
I would recommend wrapping this behavior in a HOC so you can re-use it elsewhere that you might run into this edge case (for example, i've noticed it not update the "placeholder" property of an input tag) |
Ah I get the above examples now. This is super helpful, and agree with the general sentiment in this thread on the confusion. It was difficult enough to just Google for the issue we were seeing. Thanks! |
Lost three hours in this 😿 |
Gatsby is a bag of crap. Stick with create react app |
Description
It is a little bit hard to explain, but I'll do my best. I stumbled on a following issue, although example here is simplified for the sake of replication.
When you render following component, everything works fine in development mode (
gatsby develop
).After building it, server page is rendered as expected:
But after client apps loads and hydrates, component is rerendered as
If you log
env
it will properly sayClient
in the browser's console.EDIT: It can be easily hacked by calling
setState
incomponentDidMount
, but I would like to figure out a cleaner solution. This is the old comment but even react team suggested just that in 2016.Steps to reproduce
npm run build
andnpm run serve
Expected result
Once on the client, I would expect to get this output for this specific component
Actual result
As described above, component's root element is reused and not updated.
Environment
The text was updated successfully, but these errors were encountered: