-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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
fix(gatsby-image): Don't assume DOM state is valid at hydration stage #26097
fix(gatsby-image): Don't assume DOM state is valid at hydration stage #26097
Conversation
@wardpeet requested e2e tests for the original PR:
My response:
Later, part-two of the PR which provides the CSS fix prior to React loading is a concern for adding an e2e test as it would likely mask this simpler fix. Still open to guidance with e2e if someone is happy to mentor. |
I'm still not sure where the space changes come from, but I'm not too concerned about that. I also don't remember getting an answer why regex are preferred over a string in the tests. But as they are tests, it's not a big deal to me. Fwiw, it's okay from me, so I'm leaving it to Ward to accept. Thank you for splitting this PR, it looks MUCH better for reviewing this way. I will leave this for @wardpeet (who is on vacation currently) to make sure he's okay with this. Is the description still up to date? In particular, does this slice of the PR still fix all those issues? |
A wild guess is that it's a timing issue of when Cypress takes a look? Could be that SSR CSS lacks the white-space(is there some sort of minification step there?) and after hydration, React setting styles adds those spaces. As this PR specifically modifies the rendering phase a little bit and forces a render instead of assuming the state matches (which avoids an "unnecessary" render afaik), it could very well be the reason.
Sorry, I must have missed that question. I only changed one test to a regex, I had tried to modify only the white-space in the comparison string, but recall that failed for some reason. The regex didn't and matches the other test, so that seemed fine? Did you mean testing against the whole CSS string? The each contain some values that are unique to the image data, probably wouldn't hurt doing so, but might add noise to what is actually being tested for?
Ahhh... was wondering where he disappeared to 😅
Yes, I made sure to confirm that. |
No. Just wondering why it had to be |
Is @wardpeet still on vacation? If so could I please know when he's roughly expected back? I need to know if this PR will be accepted to continue testing/development on my image caching PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you clarify how to reproduce that console error? Are you saying it's hidden in Gatsby develop and build?
The complimentary PR which is much larger adds CSS to the HTML via I just tested without the forced re-render (returned JSX must differ, hence triggering a render by hydration state bool changing alone does nothing), the problem persists. The Although, I personally don't think we should be keeping this CSS fix once hydrated, it uses |
I thought I had followed advice from the official React docs on hydration, but missed the part that it should still match the server on first pass:
If we need to return JSX that matches the server before hydration(instead of using null to force revalidation), then perhaps this would be an appropriate solution?: const imageVariants = fluid || fixed
const image = !this.state.isHydrated
? imageVariants[0]
: getCurrentSrcData(imageVariants) This way we're always using the 1st image available, and only after hydration do we update the image to adjust the inline styles. |
Prevents an initial render from hydration which can mismatch the initial state(React doesn't verify and assumes the DOM represents the same initial state it would compute). This assumption is unreliable when using Art Direction. NOTE: This change makes `componentDidMount()` fire before `handleRef()`, the `isCritical` logic will never work as the `imageRef` will not be available at this point in time.
Not entirely clear why this is necessary, but for some reason the small changes have added white-space to the CSS?
As discussed in review, keep initial state in sync with SSR for first render. At hydration `isHydrated` will trigger another render call to update the image to the correct one if different due to art direction usage. Rephrased comments, extracted shared logic from fluid/fixed conditionals (DRY). Doing so fails a test on null image data, so now additionally guard against that early.
a06a07b
to
a464f4e
Compare
Render logic was changed to be similar to how it was prior to this PR, thus these changes are no longer relevant.
da255bf
to
976012d
Compare
Since no response has been received, I went ahead and committed the change. Hopefully this is in good shape now.
If merging this, do consider #26117 and #26119. They both improve the art direction feature by addressing rendering issues users of the feature care about. I will no longer be available to make any future amendments if needed. The feedback cycle on these PRs(and engagement outside of my own PRs within this repo) is too slow for me (Initial PR opened at the start of June with a fair bit of my time invested into creating these PRs for the benefit of others, I don't use art direction at all). I understand the core team is quite busy, but the frequency I see interactions effectively abandoned is more common than I'd like. I have been thanked when helping users solve issues raised on Github or community questions/troubleshooting on the Gatsby discord when I was active there. LekoArts has been great, as was pvdz with this original PR and few others I contributed at the time. I don't benefit from these contributions, a little respect for the time taken to improve |
I apologize! This is on me, thank you for doing the work and being so patient. Gatsby-image is a difficult component to handle and you're killing it! So many thanks for it. We sadly made the decision to enable art-direction inside gatsby-image. Art-direction is not as easy as it seemed with hydration. However, we'll maintain this until the next breaking change. I'll be merging this one as it fixes the stale state. Thank you again!
This error is only shown when you use react in development mode. Sadly
|
@polarathene gatsby-image already uses double rendering when isVisible is set to true. Now double initial render.This is too expensive and here is why. For example, component that fetches images and displaying them in infinite scroll gallery. isVisible is set to true almost immediately, and that is 4 renders for each gatsby-image in short period of time. I think its better to conditionally set state in componentDidMount only if art directed feature is used as you said. |
Updates will be batched because isCritical will be true if we use loading="eager". but still for people that doesn't use eager loading |
Have you done any profiling to measure how much of a difference this PR added for you timing overhead wise? If you're not using Art Direction, you do not have a use for it. That means the
isVisible render trigger (Intersection Observer)Images first have an initial render, only browsers using Intersection Observer (Safari until afaik v14 which is releasing on Sep 16 2020) images have So for the majority of browsers/users, you'll already have one less state change triggering a new render.
|
@pindjur I've updated this PR to only modify For that PR to be merged, someone will need to finish it off(needs to handle removing listeners on unmount) and merge it. @wardpeet has agreed to that, but he is also looking into a rewrite of |
Description
If you use the Art Direction feature with image objects differing in aspect ratios(fluid) or dimensions(fixed), SSR bakes in the CSS for the first image, but the first load(hydration) expects the initial state to match the CSS, this is not always the case if your breakpoint differs from SSR, React doesn't realize it needs to update the CSS.
Notes
An improvement would be to only block the initial render for Art Directed images, although that may burden maintenance due to adding another fork in the render path.
This PR does not resolve the issue prior to React being ready, the wrong state for art directed images will still be an issue there, PR available.
WARNING: This change makes(Shouldn't apply with September commits)componentDidMount()
fire beforehandleRef()
, theisCritical
logic will never work as theimageRef
will not be available at this point in time. I have a fix for this as a part of larger PR here refactoring the behaviour it is a part of. Original PR related comment (mostly the same information).Related Issues
Fixes #25938
Fixes #24748
Fixes #16888
Fixes #16763
Related: #24811 (Original PR)