You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This PR adds lazy, lazyPreloadDistance and renderLazyPlaceholder to PagerView
With lazy, a page mounts once the pager is within lazyPreloadDistance pages of it (default 1) and stays mounted after that. Pages are tracked by key, so adding or removing pages doesn't remount the others
Test Plan
Open "Lazy PagerView Example"
Change the preload distance, swipe and use the buttons.
The mounted counter and page map show what's mounted
e2e tests pass on iOS and Android
What's required for testing (prerequisites)?
What are the steps to reproduce (after prerequisites)?
Since the last review: 0 new · 4 still open · 13 no longer raised
4 finding(s): 2 minor, 2 nit. Verdict: approve.
🟡 Minor (2)
Where
Finding
src/useLazyPages.tsx:12
Children without an element key collapse to the same pageKey "undefined" and share one mounted slot
src/useLazyPages.tsx:22
useLazyPages never updates current when the pager is moved without an onPageSelected event, leaving the visible page as a placeholder
🔵 Nit (2)
Where
Finding
src/PagerView.tsx:149
PagerView now always registers JS onPageScroll/onPageSelected handlers, forcing per-frame JS events for every caller (and breaking native-driver-only Reanimated usage)
src/useLazyPages.tsx:21
lazy is read only at first render for current (useState(initialPage)) and mountedKeys, so toggling lazy from …
Reply under an inline comment with @apex-sentinel-staging note ... or @apex-sentinel-staging pivot ... to re-check that finding.
Comment @apex-sentinel-staging review on this conversation to run a full review.
Earlier reviews
Review of 1dab22bc · time not recorded
Apex Sentinel
Verdict:request_changes
Since the last review: 10 new · 0 still open · 7 no longer raised
Lazy mounting tracks pages only on the JS side: an unmounted page can become the native current page and then nothing mounts around it
🟠 Major (1)
Where
Finding
src/useLazyPages.tsx:58
🆕 Lazy mounting tracks pages only on the JS side: an unmounted page can become the native current page and then nothing mounts around it
🟡 Minor (2)
Where
Finding
src/useLazyPages.tsx:12
🆕 Stale pages leak into mountedKeys, and pageKey can alias two different pages, so a newly added page renders another page's already-mounted content
src/useLazyPages.tsx:29
🆕 mountedKeys only ever grows: keys of removed pages are retained for the pager's lifetime
🔵 Nit (7)
Where
Finding
src/PagerView.tsx:165
🆕 The new onPageSelected wrapper is installed unconditionally
src/useLazyPages.tsx:12
🆕 pageKey folds every keyless child into the single key "undefined"
src/useLazyPages.tsx:26
🆕 lazyPreloadDistance beyond the native offscreenPageLimit unmounts a page the native pager is currently showing
src/useLazyPages.tsx:27
🆕 lazyPreloadDistance is clamped for the effective window but the example's range map uses the raw prop, so the UI can disagree with what mounts
src/useLazyPages.tsx:40
🆕 State is set during render inside useLazyPages without a guard on the cause changing, so any re-render with a changed children/preloadDistance re-derives mountedKeys
src/useLazyPages.tsx:47
🆕 Placeholder <View> reuses the page's key, so React can reuse the instance when the page mounts
src/useLazyPages.tsx:53
🆕 renderLazyPlaceholder returns content for a page index, but that content is wrapped in a plain View whose only style is the page margin
The reason will be displayed to describe this comment to others. Learn more.
No longer raised on b0040e6.
🟠 Major · useImperativeHandle recreates the ref handle only when setCurrent changes, so a null handle on mount is never replaced
useImperativeHandle's dependency list was changed from [] to [setCurrent], but setCurrent is lazy ? setCurrent : undefined (src/useLazyPages.tsx:57) — a stable value that only flips if the lazy prop itself flips. The create function (src/PagerView.tsx:110-141) reads nativeRef.current and returns null when it is still null. React only re-runs the create function when a dependency changes, so if the imperative handle is created before the native view ref is attached (or before nativeRef.current exists — the same situation the if (!instance) return null guard exists for), the caller's ref is set to null and stays null for the component's whole lifetime: every ref.current?.setPage(...) silently does nothing, and lazy mounting never advances because setCurrent is never called.
Make the imperative handle independent of setCurrent's identity: read nativeRef.current inside each method (as setScrollEnabled already does) instead of capturing instance/returning null at creation time, and call setCurrent through a ref so the deps list can go back to [].
Details
This is also inconsistent: onPageSelected can advance current (src/PagerView.tsx:165-170), so a pager whose ref never materialises still mounts pages on swipes but not on setPage.
The fix is to keep the handle always live (e.g. read nativeRef.current lazily inside each method, as setScrollEnabled already does, instead of capturing instance at creation time), and not to rely on the identity of setCurrent to refresh it.
Also raised here
Also raised at this location by senior-developer: Imperative handle memoized on an effectively constant dependency and returns null when the native ref is not yet attached — The handle factory reads nativeRef.current and returns null when it is not populated yet. Its dependency array is [setCurrent], and setCurrent is lazy ? setCurrent : undefined — a React state setter, i.e. a permanently stable identity — so in practice the factory runs exactly once per lazy toggle and the null result is never recomputed. That makes the component depend on an unstated invariant: the host ref for PagerViewNativeComponent must already be attached when useLazyPages' setter-driven re-render re-runs the hook; if a render ever produces the handle before the native view attaches (React Native re-mounts, an Animated.createAnimatedComponent wrapper as in usePagerView().AnimatedPagerView, or a future change that makes a lazy mount delay the host view), ref.current.setPage is null for the rest of the pager's life because nothing in the dependency array changes to re-run the factory. …
persona: senior-developer, javascript
Reply @apex-sentinel-staging note ... to add context, or @apex-sentinel-staging pivot ... if this is wrong — I will re-check the cited code.
Useful? Reply @apex-sentinel-staging like, or @apex-sentinel-staging dislike <why> if it is not.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
A moderate finding in src/useLazyPages.tsx shows that mountedKeys is never pruned, so keys from removed pages can be reused and mount immediately even when far from the current page.
This PR adds a lazy mode to PagerView. With lazy on, a page's content mounts only when the pager gets within lazyPreloadDistance pages of it, and it stays mounted afterward. Pages are tracked by key, so adding or removing pages does not remount the others. The change also adds a README section, an example screen, and e2e tests.
Changes:
Adds lazy, lazyPreloadDistance (default 1), and renderLazyPlaceholder props to PagerView, backed by a new useLazyPages hook that tracks which pages are mounted.
Wires setPage and page selection into the hook so jumps mount the target page and its neighbours.
Adds a "Lazy PagerView Example" screen and e2e tests that check the mounted count and scroll position.
File
Description
src/useLazyPages.tsx
New hook that decides which pages are mounted, based on the current position and preload distance.
src/PagerView.tsx
Adds the lazy props and connects scroll, selection, and setPage to the hook.
README.md
Documents the lazy props and explains placeholders and offscreenPageLimit.
example/src/LazyPagerViewExample.tsx
New example screen that shows the mounted count, the page map, and preload controls.
example/src/App.tsx
Registers the new example in the example list.
e2e/tests/lazy-pager-view.e2e.ts
New e2e tests for mounting on jump and swipe, preload distance 0, and scroll position.
The reason will be displayed to describe this comment to others. Learn more.
No longer raised on b0040e6.
🆕 🟡 Minor · Stale pages leak into mountedKeys, and pageKey can alias two different pages, so a newly added page renders another page's already-mounted content
mountNear (useLazyPages.tsx:30-38) only ever unions into the set; nothing ever prunes keys that are no longer in pages. Combined with pageKey collapsing everything to a string (useLazyPages.tsx:12-13), pages.map (line 50) matches a different child element against a key left over from an earlier child. Concretely, with keyless children Children.toArray assigns positional keys .0, .1, …, so removing the first page of [A,B,C] re-keys the list to [.0=B, .1=C] and B renders immediately because .0 is still marked mounted from A — the two elements share one identity string while React sees two different elements. The README ("Pages are tracked by key, so adding or removing pages doesn't remount the others", README.md:131) states the guarantee without the precondition that keys be unique and stable, and pages whose keys are positional (the usePagerViewpages array, src/usePagerView.ts:49-62, which removePage shrinks from the end) is exactly the shape where the invariant silently holds or silently breaks depending on which end pages are removed from. The unbounded set itself is a small leak of string keys that never shrink as pages churn.
Suggested change
Note in the README Lazy pages section that children need unique, stable keys for the "adding or removing pages doesn't remount the others" guarantee, and prune mountedKeys against the current pages keys so removed pages do not pin their keys.
Details
persona: senior-developer
Reply @apex-sentinel-staging note ... to add context, or @apex-sentinel-staging pivot ... if this is wrong — I will re-check the cited code.
Useful? Reply @apex-sentinel-staging like, or @apex-sentinel-staging dislike <why> if it is not.
The reason will be displayed to describe this comment to others. Learn more.
No longer raised on b0040e6.
🆕 🟡 Minor · mountedKeys only ever grows: keys of removed pages are retained for the pager's lifetime
mountNear builds next as a union of mounted and the nearby keys (useLazyPages.tsx:30-38) and nothing ever removes a key. onSelect/setCurrent move current, but a key whose page has left children (e.g. usePagerView's removePage, or any list that shrinks) stays in mountedKeys forever. The set is also ReadonlySet<string> state, so every mount event allocates a fresh Set copy. For a pager with a dynamically shrinking page list this is unbounded retention keyed on stale element keys, and it makes the documented promise "Pages are tracked by key, so adding or removing pages doesn't remount the others" (README.md:132) only half true: re-adding a page whose key is still in the set mounts it immediately with no placeholder, while a freshly added page gets one — the two directions are asymmetric for reasons a caller cannot see from the docs.
Suggested change
Prune keys that are no longer present in children when computing the next mounted set (or intersect with the current page keys), and note the mount-on-re-add behaviour in the Lazy pages docs section.
Details
persona: javascript
Reply @apex-sentinel-staging note ... to add context, or @apex-sentinel-staging pivot ... if this is wrong — I will re-check the cited code.
Useful? Reply @apex-sentinel-staging like, or @apex-sentinel-staging dislike <why> if it is not.
The reason will be displayed to describe this comment to others. Learn more.
No longer raised on b0040e6.
🆕 🔵 Nit · pageKey folds every keyless child into the single key "undefined"
Children.toArray guarantees keys only for valid elements; a raw string/number child (which <PagerView>child</PagerView> accepts as children) yields pageKey === 'undefined', so all such pages share one entry in mountedKeys and mount/unmount together. Pages are documented as needing a key (README "Pages are tracked by key"), so this is reachable only by misuse — falling back to the index, e.g. ?? String(index), would make the degenerate case behave per-page instead of as one shared page.
The reason will be displayed to describe this comment to others. Learn more.
No longer raised on b0040e6.
🆕 🔵 Nit · lazyPreloadDistance beyond the native offscreenPageLimit unmounts a page the native pager is currently showing
useLazyPages.tsx:27 derives distance purely from preloadDistance, and PagerView passes the children prop straight through to the native component, so a page that leaves the mounted window is replaced in place by <View>{renderPlaceholder?.(index)}</View> (useLazyPages.tsx:49-55) — the child's content is swapped while the native pager may still be compositing it. Android's offscreenPageLimit defaults to 1 (src/PagerViewNativeComponent.ts:26 has no explicit default documented here, README.md:109 says "Defaults to 1", and the README's own Lazy section at 154 stresses it is "unrelated" to lazy). So <PagerView lazy lazyPreloadDistance={0} offscreenPageLimit={1}> — a configuration the README never warns against, and the lazy example does not use — unmounts the neighbour page that the pager keeps composed for the transition: the user dragging one page sees real content on one side and a placeholder on the other, then the content pops in on settle. Nothing in useLazyPages or PagerView ties distance to offscreenPageLimit or takes the max of the two.
Suggested change
Take the effective distance as max(lazyPreloadDistance, offscreenPageLimit ?? 1) in PagerView before calling useLazyPages, or document that lazyPreloadDistance must be >= offscreenPageLimit on Android.
Details
persona: senior-developer
Reply @apex-sentinel-staging note ... to add context, or @apex-sentinel-staging pivot ... if this is wrong — I will re-check the cited code.
Useful? Reply @apex-sentinel-staging like, or @apex-sentinel-staging dislike <why> if it is not.
The reason will be displayed to describe this comment to others. Learn more.
No longer raised on b0040e6.
🆕 🔵 Nit · lazyPreloadDistance is clamped for the effective window but the example's range map uses the raw prop, so the UI can disagree with what mounts
useLazyPages computes the window as Math.max(preloadDistance, 0) + 1 (useLazyPages.tsx:27) and the hook itself takes no lower/upper validation of the prop: PagerViewProps.lazyPreloadDistance is a plain number with a default of 1 (PagerView.tsx:45,92). A caller passing a negative value gets distance = 1 (current page only, i.e. silently the same as 0) rather than an error, and a caller passing a fractional value such as 1.5 gets a window of 2 (an asymmetric, silently-rounded range). The public prop is documented in README.md:103 as "how many pages on each side … Defaults to 1", which neither of those behaviours matches. There is no validation or rounding at the prop boundary — the coercion is buried inside the +1 expression, so the documented semantics and the implemented window can diverge for any value that is not a non-negative integer.
Normalise lazyPreloadDistance once at the public boundary (Math.max(0, Math.round(n))) so the documented "pages on each side" matches the implemented window for negative and fractional inputs, and say so in the prop JSDoc.
Details
persona: javascript
Reply @apex-sentinel-staging note ... to add context, or @apex-sentinel-staging pivot ... if this is wrong — I will re-check the cited code.
Useful? Reply @apex-sentinel-staging like, or @apex-sentinel-staging dislike <why> if it is not.
The reason will be displayed to describe this comment to others. Learn more.
No longer raised on b0040e6.
🆕 🔵 Nit · Placeholder <View> reuses the page's key, so React can reuse the instance when the page mounts
useLazyPages returns a fresh children array on every PagerView render, and for the default non-lazy path that array is props.children itself. That array identity is now what childrenWithOverriddenStyle (src/utils.tsx:8, src/utils.ios.tsx:8) iterates with Children.map, and the result feeds the native children prop of RNCViewPager. Two consequences:
Suggested change
return{
<Viewkey={`placeholder:${pageKey(page)}`}>
{renderPlaceholder?.(index)}
</View>
Suggested change
Give the placeholder a key distinct from the page's key (or a non-View wrapper type) so React cannot reuse the placeholder instance as the mounted page.
Details
Children.map skips nil/false/undefined children. A consumer using {cond && <Page/>} or pages.map(...).filter(...) style children already got the page rendered, but the array this hook hands over now comes from useLazyPages rather than being the raw props.children, so any conditional child is dropped from the pager rather than just rendering nothing — the same skipping existed before, so this half is not new; the new half is below.
More concretely: in lazy mode a page is replaced in place by <View key={pageKey(page)}>{renderPlaceholder?.(index)}</View> (src/useLazyPages.tsx:53). childrenWithOverriddenStyle clones that element with style: [element.props.style, StyleSheet.absoluteFill] and React.cloneElement on a <View> whose only child is the placeholder output. When the page mounts, React reconciles placeholder-View against the user's page element. For a page whose element type is View (the documented shape: "It is important all children are <View>s", src/PagerView.tsx:74), the type and key match, so React reuses the instance and the page's subtree mounts into the still-present placeholder children — which childrenWithOverriddenStyle also re-wraps in the absoluteFill wrapper. The result is that placeholder content and page content coexist in one reused wrapper for the transition, and anything renderLazyPlaceholder returns with its own state/animation (the README example returns <ActivityIndicator />) keeps running inside the mounted page instead of unmounting.
persona: senior-developer
Reply @apex-sentinel-staging note ... to add context, or @apex-sentinel-staging pivot ... if this is wrong — I will re-check the cited code.
Useful? Reply @apex-sentinel-staging like, or @apex-sentinel-staging dislike <why> if it is not.
The reason will be displayed to describe this comment to others. Learn more.
No longer raised on b0040e6.
🆕 🔵 Nit · renderLazyPlaceholder returns content for a page index, but that content is wrapped in a plain View whose only style is the page margin
For an unmounted page the hook returns <View key={...}>{renderPlaceholder?.(index)}</View>, and childrenWithOverriddenStyle then overwrites that View's style with { marginHorizontal: pageMargin } (src/utils.tsx:47) — no flex: 1. The README example (renderLazyPlaceholder={() => <View style={{flex: 1, justifyContent: 'center'}}><ActivityIndicator /></View>}) only fills the pager because the outer wrapper never stretches, so with pageMargin set the placeholder is additionally shifted sideways and vertically centered over a zero-flex container, which can read as an empty page. Documented behavior ("what a page shows before its content is mounted") therefore depends on the consumer adding a style the wrapper cannot carry. Either give the wrapper {flex: 1} and merge rather than overwrite in childrenWithOverriddenStyle, or say in the renderLazyPlaceholder jsdoc that the returned node is wrapped in a flex-less View.
Make the placeholder wrapper flex: 1 (or merge styles instead of overwriting them in childrenWithOverriddenStyle) so a placeholder that uses flex: 1 actually fills the page, and note the wrapper in the prop's doc comment.
Details
persona: maintainability
Reply @apex-sentinel-staging note ... to add context, or @apex-sentinel-staging pivot ... if this is wrong — I will re-check the cited code.
Useful? Reply @apex-sentinel-staging like, or @apex-sentinel-staging dislike <why> if it is not.
Apex Sentinel could not review commit 7a766d4a. No review ran, so there are no findings; this is not a verdict on the change.
Apex Sentinel could not read or check out this pull request, which is usually temporary (GitHub was slow, or the branch moved while the review waited). If it keeps failing, tell whoever runs Sentinel for this repository, or check that the App still has access to it. Push a new commit or comment @apex-sentinel-staging review to try again.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
A moderate finding in src/useLazyPages.tsx shows the selected page is only tracked while lazy is enabled, so turning it on after the user has moved can leave a stale index and a placeholder on the visible page until the user scrolls again.
No open issues 🎉
1 previously missed issue
In code that hasn't changed since last review
Track selected page before lazy loading is enabled
src/useLazyPages.tsx:72
current only updates while lazy is on: setCurrent is undefined and onSelect ignores selections otherwise. If lazy is turned on after the user has moved, mountNear starts from a stale index. The visible page can then show its placeholder until the user drags again. Track the selected page unconditionally, and keep the lazy check only around the mounting in onScroll. This adds a state update, and so a re-render of PagerView, on every page change, including for pagers that don't use lazy. If that cost matters, store the page in a ref instead.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Minor · useLazyPages never updates current when the pager is moved without an onPageSelected event, leaving the visible page as a placeholder
current is only ever changed by setCurrent (imperative setPage) and onSelect (useLazyPages.tsx:61-72); the mount-range recomputation during render (lines 44-49) uses that value. Two reachable sequences leave it stale:
Re-anchor current when it can drift: derive the active position from onPageScroll (the hook already receives position/offset) instead of only from onPageSelected/setCurrent, or track the lazy prop transition and recompute the mount range against the pager's actual position when lazy flips to true.
Details
A caller toggles lazy to true after mount while the pager sits on page N (e.g. a useState(false) flipped after the first screen). current is still initialPage, so mountNear mounts the pages around initialPage only; the page actually on screen renders renderLazyPlaceholder and keeps rendering it until some lateronPageSelected event arrives, which never happens while the user stays on the page — so a permanently blank/placeholder screen with lazyPreloadDistance={0} (where the current page is not in the mount range). Nothing re-syncs current, because initialPage is read only for the initial useState value and onPageScroll-driven mounting is skipped (it is gated on isDragging).
A programmatic page change that does not fire onPageSelected (a native setCurrentItem without an event, or an event dropped because the JS thread was busy) permanently desynchronises current from the real position, so every later lazy mount window is computed around the wrong page — the visible page can be replaced by a placeholder on the next onSelect-driven remount of the range.
The hook should derive the position from the events it already receives (it sees onPageScroll positions in onScroll) or at least re-anchor when lazy turns on, instead of trusting that onPageSelected always fires.
persona: senior-developer
Reply @apex-sentinel-staging note ... to add context, or @apex-sentinel-staging pivot ... if this is wrong — I will re-check the cited code.
Useful? Reply @apex-sentinel-staging like, or @apex-sentinel-staging dislike <why> if it is not.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Nit · PagerView now always registers JS onPageScroll/onPageSelected handlers, forcing per-frame JS events for every caller (and breaking native-driver-only Reanimated usage)
To feed useLazyPages, PagerView unconditionally passes onPageScroll and onPageSelected to the native component (src/PagerView.tsx:192-194), even when the consumer passed neither prop. In React Native, whether a native view dispatches an event across the bridge/JSI is decided by whether the handler prop is non-null, so after this change every mount of PagerView — lazy or not, and lazy defaults to false — starts emitting onPageScroll on every scroll frame and onPageSelected to the JS thread. Callers that previously rendered <PagerView style={{flex:1}}> with no event handlers (or with Animated.event(..., {useNativeDriver: true}) only, as the Reanimated guide in docs/docs/docs/guides/reanimated.md documents) pay a per-frame JS event dispatch they did not pay before, i.e. a performance regression for the entire library, exactly in the scenario native-driver events exist to avoid. The handlers also run even when lazy === false, where onScroll/onSelect are no-ops, so the cost buys nothing for non-lazy users. Gate the added handlers on lazy so that, without lazy, the props stay exactly props.onPageScroll/props.onPageSelected as before.
Only attach the wrapping onPageScroll/onPageSelected handlers when lazy is enabled, and note in the README Lazy pages section that lazy requires JS-thread delivery of onPageScroll, so it should not be combined with a UI-thread-only (Reanimated useEvent / useNativeDriver) event handler.
Details
Also raised here
Also raised at this location by javascript: onPageScroll / onPageSelected are now always registered natively, even for callers that pass no handler — Before this PR the native component received exactly the handlers the app passed: the {...props} spread carried onPageScroll / onPageSelected, so a caller that passed neither left those registration names unset and the native side never emitted them (the view managers register them at android/src/main/java/com/reactnativepagerview/PagerViewViewManager.kt:128-130, and iOS emits through eventEmitter->onPageScroll(...) at ios/RNCPagerViewComponentView.mm:111-130). Failure mode: every existing <PagerView> that passes no scroll handler now has both events registered natively, so the pager dispatches an onPageScroll event across the bridge on every scroll frame of every swipe and React re-renders PagerView for each of them — for callers that never observe scroll events on the JS thread (e.g. those that only drive native-driver animations). …
Also raised at this location by maintainability: onScroll runs on every native onPageScroll event while dragging — many per second — and each call allocates a new Set… — onScroll runs on every native onPageScroll event while dragging — many per second — and each call allocates a new Set over all page keys and schedules a PagerView state update (src/useLazyPages.tsx:60-65 via src/PagerView.tsx:146-152). With 20+ pages and a heavy page subtree this is the JS-thread work the README itself warns about; throttle to the integer position (skip the update when Math.round/position+offset rounds to the same page as last time) so the Set is only rebuilt when the mounted range would actually change.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Nit · lazy is read only at first render for current (useState(initialPage)) and mountedKeys, so toggling lazy from …
lazy is read only at first render for current (useState(initialPage)) and mountedKeys, so toggling lazy from false to true on an existing pager starts from the initial page's range rather than the page the user is on, and the previously-rendered real children are replaced by placeholders. If toggling isn't supported, say so in the LazyProps doc comments; otherwise sync current from onPageSelected on the first lazy render.
Details
persona: maintainability
Reply @apex-sentinel-staging note ... to add context, or @apex-sentinel-staging pivot ... if this is wrong — I will re-check the cited code.
Useful? Reply @apex-sentinel-staging like, or @apex-sentinel-staging dislike <why> if it is not.
This branch has not been deployed
No deployments
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
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
This PR adds
lazy,lazyPreloadDistanceandrenderLazyPlaceholdertoPagerViewWith
lazy, a page mounts once the pager is withinlazyPreloadDistancepages of it (default 1) and stays mounted after that. Pages are tracked by key, so adding or removing pages doesn't remount the othersTest Plan
e2e tests pass on iOS and Android
What's required for testing (prerequisites)?
What are the steps to reproduce (after prerequisites)?
Compatibility
Checklist
README.md