feat: rewrite core with function component and IntersectionObserver sentinel#422
Merged
iamdarshshah merged 11 commits intomasterfrom Apr 12, 2026
Merged
feat: rewrite core with function component and IntersectionObserver sentinel#422iamdarshshah merged 11 commits intomasterfrom
iamdarshshah merged 11 commits intomasterfrom
Conversation
Replace rollup.config.js with rollup.config.mjs, swap rollup v1 plugins for @rollup/plugin-node-resolve and @rollup/plugin-typescript, and remove the throttle-debounce runtime dependency.
…ntinel Replace class component + scroll listeners with a function component that uses an IntersectionObserver on an invisible sentinel div. Adds buildRootMargin utility to convert scrollThreshold into a CSS rootMargin string. Drops throttle-debounce usage entirely.
Migrate all tests to @testing-library/react, add IntersectionObserver mock in setup/, and add new test files for buildRootMargin utility and no-scrollbar behaviour.
The previous code pushed a nested array instead of spreading items, causing incorrect data shape in the Storybook window scroll story.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #422 +/- ##
===========================================
+ Coverage 79.33% 95.03% +15.69%
===========================================
Files 2 3 +1
Lines 150 161 +11
Branches 56 57 +1
===========================================
+ Hits 119 153 +34
+ Misses 24 4 -20
+ Partials 7 4 -3 🚀 New features to boost your workflow:
|
|
Size Change: -417 B (-4.85%) ✅ Total Size: 8.19 kB 📦 View Changed
|
… null scrollableTarget, and missing refreshFunction
… mode, initialScrollY via scrollableTarget Adds 7 tests covering: - PTR: scrollTop > 0 guard blocks drag start - PTR: upward pull (currentY < startY) ignored - PTR: delta capped at 1.5× maxPullDownDistance - PTR: releaseToRefreshContent shown when threshold breached - PTR: window-scroll mode (no height) — listeners on window, exercises document.documentElement.scrollTop branch - onScroll: window fallback when no height or scrollableTarget - initialScrollY: scrolls scrollableTarget element (no height prop path)
- Fix inverse sentinel placement: sentinel now renders before children when inverse=true so the IO top-margin fires correctly when scrolling up - Add SSR guard: typeof IntersectionObserver === 'undefined' check in Effect 2b prevents crash in Next.js App Router server components - Fix onScroll prop type: MouseEvent -> UIEvent (scroll events are UIEvents) - Fix defaultThreshold.value: 0.8 -> 80 (was computing 99.2% rootMargin instead of 20% on invalid scrollThreshold input) - Add exports field to package.json for Node ESM and Next.js 13+ App Router subpath resolution; main/module kept for older bundler fallback - Bump version to 7.1.0 - Fix pre-existing ts-check breakage: replace global with globalThis in test files, add resolveJsonModule for JSON import, exclude stories from tsconfig (storybook types not installed in devDependencies)
- rollup.config.mjs: switch tsconfig from tsconfig.json to tsconfig.lib.json tsconfig.json includes root-level JS files (lint-staged.config.js, jest.config.js), making TypeScript compute rootDir as the project root and emit to dist/src/index.js. @rollup/plugin-typescript uses ts.getOutputFileNames which returns dist/index.js, so emittedFiles lookup always misses and the plugin falls through without transpiling. tsconfig.lib.json includes only src files, rootDir is inferred as src/, and both paths agree on dist/index.js. - .eslintrc.js: ignore src/stories since tsconfig.json now excludes it; @typescript-eslint/parser requires project-included files to parse type-aware rules. - pullDown.test.tsx: global -> globalThis to fix no-unsafe-member-access lint error.
… in column-reverse layout
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
InfiniteScrollfrom a class component to a function component usinguseRef/useEffect/useStateIntersectionObserversentinel pattern, a small invisible div at the end of the list is observed;next()is called when it enters the viewportthrottle-debounceas a runtime dependency (zero runtime deps)@rollup/*scoped packagesscrollableTargetprop type (ReactNode→HTMLElement | string | null)IntersectionObservermock for jsdom, new test files forbuildRootMargin,noScrollbar, andscrollableTargetWhy IntersectionObserver
IO is purpose-built for visibility detection, it runs off the main thread and doesn't require throttling. Benchmarks show ~23% scripting time vs ~29% for throttled scroll listeners. All major pure infinite scroll libraries have moved to IO; virtualization libraries (react-window, TanStack Virtual) remain on scroll events because they need real-time scroll position data, which is a different problem.
What is unchanged for consumers
All existing props work identically,
next,hasMore,dataLength,loader,endMessage,scrollThreshold,height,scrollableTarget,inverse,pullDownToRefresh,onScroll,initialScrollY,className. No props were removed or renamed. This is a v7 minor.Test plan
yarn test: 54 tests, all passingyarn build: CJS, ESM, and IIFE outputs producedyarn size: bundle within the 6 kB limits set by the quality gatesscrollableTarget, pull-to-refresh, inverse mode