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

fix(web): invalidate position on ancestor scroll #472

Merged
merged 2 commits into from
Jan 13, 2023

Conversation

motiz88
Copy link
Contributor

@motiz88 motiz88 commented Dec 27, 2022

Summary:

Fixes #471.

As there is no native way to listen for changes to getBoundingClientRect(), the web implementation of Slider must cache and try to correctly invalidate the measurements. The code already does this on layout and when the viewport is resized, but not when the slider's bounding rect changes due to scrolling. This PR registers a capturing scroll event handler on document and invalidates the cache when an ancestor of the slider scrolls. This is probably not airtight (see this StackOverflow discussion) but is AFAICT correct.

For performance, it may be worth experimenting with unregistering the scroll (and resize) listener once the cache has been invalidated - this would avoid calling O(sliders on page) handlers on every frame while scrolling. This is a potentially more invasive change to Slider so I am publishing the PR without it.

Test Plan:

Added an example to example-web and tested it manually (see video).

Screen.Recording.2022-12-27.at.20.53.08.mov

Fixes callstack#471. Added a test case to `example-web`.
@motiz88 motiz88 marked this pull request as ready for review December 27, 2022 21:25
//@ts-ignore
window.addEventListener('resize', onResize);
window.addEventListener('resize', invalidateContainerPosition);
Copy link
Contributor

Choose a reason for hiding this comment

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

@motiz88 you should probably subscribe to resize events the react-native way:

https://necolas.github.io/react-native-web/docs/use-window-dimensions/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jspizziri

  1. This use of the resize event was pre-existing and I had no intention of changing it as part of this bugfix. I merely renamed onResize to invalidateContainerPosition to better communicate what it does (and reuse it in the onScroll handler).
  2. IMO, given this is platform-specific code, use of DOM primitives is completely fine. It's also not directly interchangeable with useWindowDimensions (which would tie us to the React component lifecycle in a way that might not be the most correct/efficient for this use case)

window.addEventListener('resize', onResize);
window.addEventListener('resize', invalidateContainerPosition);
//@ts-ignore
document.addEventListener('scroll', onDocumentScroll, {capture: true});
Copy link
Contributor

Choose a reason for hiding this comment

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

If there's no way to do this in a more react-native idiomatic way, my opinion would be to do something like the following at the top of the file:

// @ts-ignore
const { addEventListener, removeEventListener } = document;

...
const RCTSliderWebComponent = React.forwardRef(
...
);

That would reduce the overall number of @ts-ignore comments in the code, which aren't ideal.

Perhaps it would be even better to create a unique hook (e.g. useOnDocumentScroll that receives a callback or something), that abstracts this functionality.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That would reduce the overall number of @ts-ignore comments in the code, which aren't ideal.

I think the @ts-ignore issue (which, again, is pre-existing here) would be best solved by configuring TypeScript to properly typecheck uses of DOM APIs in .web.tsx files. This might be solved by adding /// <reference lib="dom" /> to the top of the file, but I haven't tried it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jspizziri I've put up #475 to deal with the @ts-ignores comprehensively.

Copy link
Member

@BartoszKlonowski BartoszKlonowski left a comment

Choose a reason for hiding this comment

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

To me it looks very good, I'm happy to accept that. Also thanks so much for adding another example showing this specific scenario! 👍

@jspizziri Would it be possible for you to let us know if @motiz88's answers address your remarks? I don't want to merge something that still has some potentially open discussions.

package/src/RNCSliderNativeComponent.web.tsx Outdated Show resolved Hide resolved
@jspizziri
Copy link
Contributor

@BartoszKlonowski LGTM!

@BartoszKlonowski BartoszKlonowski merged commit ccb2d42 into callstack:main Jan 13, 2023
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.

Slider doesn't work in a horizontal scrolling container on web (e.g. tab view)
3 participants