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

chore(gatsby-image): lazy-load refactor #14158

Merged

Conversation

polarathene
Copy link
Contributor

@polarathene polarathene commented May 19, 2019

Description

Light Refactor around the recent changes from adding native lazy-loading support. For some reason most of my review on the PR was ignored(even after requesting feedback after a couple weeks of inactivity).

Best reviewed via individual commits for clarity and each commits message detailing what was done.

Prettier in some cases made some changes less pretty. Related issue on Prettier repo.


Questions / Concerns

if (typeof critical === `boolean` && process.env.NODE_ENV !== `production`) {
    logDeprecationNotice(`critical`, `the native "loading" attribute`)

    convertedProps.loading = critical ? `eager` : loading || `lazy`
}

~~Do you really want to only map critical to loading in dev, not production? Or did you only want to raise the deprecation notice in dev? ~~

Is there any reason to use a ternary? Shouldn't it just check if critical is true?

if (critical === true) {
    logDeprecationNotice(`critical`, `the native "loading" attribute`) // Move the env check into this method
    convertedProps.loading = `eager` // If loading wasn't already `lazy` it will be `auto` and the browser can probably make a more informed decision if using eager or lazy is more appropriate.
}

EDIT: Above concern has been addressed. Same as the suggestion above, but no strict equality check since other conditions on the prop just check for truthiness.


critical is now advised against being used to have usage replaced with loading attribute instead via this message and docs. However critical is still relevant in the Image constructor and componentDidMount(), this advice may cause issues to be opened due to confusion as the prop is not 1:1 replacement atm.

imgCached will be false in situations that IO isn't used for lazy-load, thus shouldFadeIn won't be disabled, which afaik means we'll probably run into #12254 again?

polarathene added 8 commits May 19, 2019 18:04
This seems it was intended for discussion when implementing. It doesn't have value in the source code. 

There is no special handling needed, no concern to make a maintainer aware of, nor is a default set like stated, only when the prop exists.
There is no need to pass in a prop to indicate native lazy-load support, that is inferred by the `loading` prop.

There is no need to have conditional checks and a spread operator, just assign the prop value to `loading`. If the prop doesn't exist(loading==false), it's ignored by React.
Extracted feature detection to vars to avoid the interleaving logic flipping booleans, especially with `isVisible`.

`hasNoScript` => `addNoScript`
`IOSupported` => `hasIOSupport`/`useIOSupport`
`nativeLazyLoadSupported` => `hasNativeLazyLoadSupport`

This clarifies intentions, reducing complexity and thus increasing readability of the entire flow.

`isVisible` is false during SSR(avoiding being rendered into markup prior to hydration) unless `critical` is present).

`isVisible` is true upon hydration when `critical` is present, or in a browser that has native lazy-load support or when IO support is not available. 

If false upon hydration, the markup lacks the main `<picture>` element until `isVisible` is true, such as when triggered by an IO event.
`render()` should not be calling `convertProps()` twice to handle overwriting / mapping `loading` based on `critical`. That is what `convertProps()` is specifically for. 

Moved logic there and moved the deprecation notice into a reusable function. Perhaps the `resolutions` and `sizes` legacy props should also utilize it?

Removed the comment, based on new context it should be clear.

Also avoided overwriting `loading` if `critical==false`, `auto` is a possible value for `loading`. The || fallback shouldn't ever be hit AFAIK?
No point running this logic for each class instance?
As the method is only for lazy-loading with IO support, there is no need to bail early, `useIOSupport` just needs to be false if native lazy-load support exists.
Additionally, remove the original `isVisible` declaration (now a duplicate), and a small refactor in `convertProps()`

Note the formatting changes that Prettier requested seem to have the opposite effect of being more readable/maintainable.
This PR is failing tests due to this, not sure how this didn't raise a failure in it's own PR?
@polarathene
Copy link
Contributor Author

polarathene commented May 20, 2019

Dealing with the failing tests atm. The recent PR for a customizable durationFadeIn somehow passed the tests, I'm confused why, as they should have picked up on the problem with a string value in the test?

That's the only error failing jest from master, I've got a commit for it in this PR to push addressing it.

$ jest ./packages/gatsby-image
 PASS  packages/gatsby-image/src/__tests__/index.js
  <Image />
    ✓ should render fixed size images (44ms)
    ✓ should render fluid images (7ms)
    ✓ should have correct src, title, alt, and crossOrigin attributes (10ms)
    ✓ should have correct placeholder src, title, style and class attributes (5ms)
    ✓ should have a transition-delay of 1sec (9ms)
    ✓ should call onLoad and onError image events (13ms)

  console.error node_modules/prop-types/checkPropTypes.js:19
    Warning: Failed prop type: Invalid prop `durationFadeIn` of type `string` supplied to `Image`, expected `number`.
        in Image (at __tests__/index.js:33)

3 jest snapshot errors due to loading="lazy" now appearing. My PR has changed behaviour from master here, which means I may have made a mistake or corrected the behaviour. Investigating.

UPDATE: Seems warnings can pass the unit tests, I only noticed it due to snapshot mismatch error.

Updated snapshot, it should include the loading attribute regardless since the prop is given a default of lazy. We could omit that and trust the browser to decide when eager or lazy is most appropriate, unless the user provides an explicit choice.

Do you want the test verifying the default lazy value implicitly, or would you like the test to include eager or auto via the loading prop instead with an expect() rule to verify?

`loading="lazy"` should be in the snapshot, which believes it's in a browser(so not SSR), but no IO or lazy-load support, thus `isVisible` is true as expected.

The original feature PR avoided the attribute if the feature was not supported by the browser(but includes it in noscript), no negative impact from including it.
Copy link
Contributor

@wardpeet wardpeet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome! This looks really good!

test to include eager or auto via the loading prop instead with an expect() rule to verify?

I would do this also check if critical is mapping on eager.

packages/gatsby-image/src/index.js Outdated Show resolved Hide resolved
packages/gatsby-image/src/index.js Show resolved Hide resolved
packages/gatsby-image/src/index.js Show resolved Hide resolved
polarathene added 2 commits May 20, 2019 19:14
Move the environment check into `logDeprecationNotice()` which is what it was most likely intended for.

`critical` is already typed as a boolean and treated as one elsewhere, no need to do a type check, it's not polymorphic like `bgColor`.

Treatment only needs to assign `eager` as the `loading` attribute is already set as `lazy` by default.
polarathene added 3 commits May 20, 2019 22:09
Turns out the props passed in are immutable, hence why they were copied with `{ ...props }` in the first place :)
Verify default is `lazy`,  test `critical` prop sets `eager`.
As requested: gatsbyjs#14158 (comment)

`useIOSupport`,`addNoScript`,`seenBefore` all become properties of the `Image` class. `hasNativeLazyLoadSupport` isn't used anymore, other than in the constructor.

Reduced the state calls in `handleImageLoaded()` to 1.

Changed the order of `shouldReveal` to align with `shouldFadeIn`.
@polarathene polarathene changed the title chore(gatsby-image) lazy-load cleanup chore(gatsby-image) lazy-load refactor May 20, 2019
There doesn't appear to be purpose in delaying until `handleImageLoaded()`?

`seenBefore` would not change, someone might change `fadeIn` to true inbetween but I can't think of why we'd want to override that?

Added `isBrowser` guard mainly to communicate more clearly that it has no effect in SSR(image needs to load before it can be true, regardless of duplicates during SSR).

Since the initial vars set up top aren't used, I placed them in the state directly.
@polarathene polarathene force-pushed the chore/gatsby-image-lazyload-cleanup branch from c923232 to cd6204b Compare May 20, 2019 12:38
@polarathene polarathene requested a review from wardpeet May 22, 2019 13:11
@polarathene
Copy link
Contributor Author

This should be good to merge, I still need to address the two concerns pointed out at the top after my edit, but I'm a bit busy for the rest of the month.

@wardpeet
Copy link
Contributor

Looks good! thanks for updating. Doing a test run and it will be ready to merge.

Copy link
Contributor

@wardpeet wardpeet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Thanks for cleaning up & thanks for your patience!

@wardpeet wardpeet changed the title chore(gatsby-image) lazy-load refactor chore(gatsby-image): lazy-load refactor May 27, 2019
@wardpeet wardpeet merged commit 4379780 into gatsbyjs:master May 27, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants