Skip to content

Debounce/throttling support #10

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
wrench7 opened this issue Jul 20, 2018 · 6 comments
Closed

Debounce/throttling support #10

wrench7 opened this issue Jul 20, 2018 · 6 comments

Comments

@wrench7
Copy link

wrench7 commented Jul 20, 2018

Firstly, fantastic job with this library! Love that it's built directly in Typescript :-)

Wondering how hard it would be to add a debounce/throttle option so that only images that stay within the viewport for a certain amount of time actually trigger the loading of their content?

@fpapado
Copy link
Owner

fpapado commented Jul 21, 2018

Hi, @wrench7! Thanks for your kind words.

I've thought about this feature, and have something similar listed in the Roadmap.
Debouncing is not possible per se, because the IntersectionObserver API is asynchronous and its scheduling not straightforward. Still, given that this component monitors the image loading, I think we might be able to work a configurable minimum delay into that.

Implementation Sketch

The steps would be something like this:

- IntersectionObserver says the component is in view

- Kick off image loading (a Promise) after a minimum delay 
  Promise, if specified (e.g. p-min-delay)

- If IntersectionObserver says the component is out of view, 
  and the delay Promise is not resolved, discard it. Set state
  back to "NotAsked"

- Otherwise, let things load as they are.

There are some tricky bits that might make this hard to get right:

  • Promise cancellation is not really a thing afaik. So we should ensure that loading truly does get cancelled / the chain is broken.
  • I think it should be two chained Promises; the "Delay Promise" and the "Image Promise".
    • The "Delay Promise" is the one we would aim to cancel.
    • The "Image Promise" should probably stay intact, to avoid wasting downloads.
  • Signaling the "out-of-view" and "did not load, keep observing" would require changes to how the component uses IntersectionObserver.
  • Communicating that this delay is a "minimum" is probably important for users of the library.

I'll play around with this and see how it shapes up.

Here's a simple test case to check any implementations with:
https://codesandbox.io/s/3215xr7615

@wrench7
Copy link
Author

wrench7 commented Jul 23, 2018

Wow, what a fantastic response! :-)

Your implementation sketch pretty much nails it I think. The test case looks good as well.

Bit of an aside, but I could have sworn I've seen Chrome cancelling image requests when components are removed, but can't seem to see that in the dev tools now, nor find any results when googling for things along the lines of "react cancel image load". That may not be relevant I guess if all you're thinking about doing is cancelling the initial Delay Promise, and once things have progressed to the Image Promise it's going to load regardless of where the viewport is.

If you were going to go down the promise cancelling path, this appears to be a sound approach: https://stackoverflow.com/a/37492399
Using a 3rd party Promise library like Bluebird, or RX observables would also work, but would probably be overkill and a lot of work to shift over to either of those.

Let me know if I can help out in anyway as well! :-)

EDIT: I just made the changes to the underlying code and used a fairly simplistic setTimeout/clearTimeout for the delay aspect. Code changes were minimal going down this path.

@fpapado
Copy link
Owner

fpapado commented Jul 24, 2018

Hi @wrench7, I managed to get it working 🎉

The code at https://codesandbox.io/s/3215xr7615 uses react-lazy-images@next, atm [email protected].

Release timing

I have a few more features pending for that release (LazyPicture, hopefully), and definitely a bit of cleanup. Specifically, I'm trying to make the interface non-breaking, and rc.2 needs a change for LazyImageFull. Still, you can try it out and follow the release notes for v1.1.0 when it's out :)

External API

The external interface is the same, with an optional debounceDurationMs={/* some number */} prop.

Internals

Thanks for the cancellation reference, it was helpful to get the internal API where I wanted. I am not really a fan of promises, but one of the interfaces returns one already (loading the image), so it made sense to me to use one for the delay, simple as it might be. (Their property of always resolving with the same result also seems useful to React, one of their next features depends).

I'm curious what the setTimeout/clearTimeout solution looks like. React usually gives me dev warnings about "task scheduled in render" with those 🤔

There was a refactor of the codebase, particularly to isolate areas where we kick off side-effects (buffering, image requests) and make the various states and transitions explicit. It's where it was headed with the internal state, and adding an extra "Buffering" state tipped the scales. The code grew a bit, but I like it much more now; it makes good use of types imo.

The source is on the feature-delayed-loading branch, if you're interested:
https://github.com/fpapado/react-lazy-images/tree/feature-delayed-loading

@wrench7
Copy link
Author

wrench7 commented Jul 26, 2018

Hi Fotis :-)

Awesome work with that update. I took the codesandbox sample for a spin and it works very nicely.

I'll hold off using the RC until you're happy with the changes.

re: the setTimeout solution I hacked together - you're right I now get the occasional "Can only update a mounted or mounting component" error in the console, but this doesn't appear to have an adverse affect on the rendering. Obviously your solution won't have that issue so I look forward to having a play with it.

Thanks again!

@fpapado
Copy link
Owner

fpapado commented Aug 5, 2018

Hi! Quick ping to let you know that I've published 1.1.0 on npm. LazyPicture was taking a while, and saw no need to delay this with holidays around the corner. Hope it helps!

@wrench7
Copy link
Author

wrench7 commented Aug 6, 2018 via email

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

No branches or pull requests

2 participants