This commit is contained in:
Dan Abramov
2023-12-15 06:07:43 +00:00
parent c01243620a
commit 534427d234
+60 -49
View File
@@ -1,5 +1,5 @@
import React, {memo, useRef, startTransition} from 'react' import React, {memo, useRef, startTransition} from 'react'
import {FlatListProps, StyleSheet, View} from 'react-native' import {FlatListProps, StyleSheet, View, ViewProps} from 'react-native'
import {addStyle} from 'lib/styles' import {addStyle} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
@@ -114,8 +114,8 @@ function ListImpl<ItemT>(
}, [isVisible]) }, [isVisible])
const isScrolledDown = useRef(false) const isScrolledDown = useRef(false)
function handleScrolledDownDetectorVisibleChange(isMarkerVisible: boolean) { function handleAboveTheFoldVisibleChange(isAboveTheFold: boolean) {
const didScrollDown = !isMarkerVisible const didScrollDown = !isAboveTheFold
if (isScrolledDown.current !== didScrollDown) { if (isScrolledDown.current !== didScrollDown) {
isScrolledDown.current = didScrollDown isScrolledDown.current = didScrollDown
startTransition(() => { startTransition(() => {
@@ -125,47 +125,45 @@ function ListImpl<ItemT>(
} }
return ( return (
<View style={{paddingTop: 0}}> <View {...props} style={style} ref={nativeRef}>
<View {...props} style={style} ref={nativeRef}> <Visibility
<View onVisibleChange={setIsVisible}
style={[ style={styles.parentTreeVisibilityDetector}
styles.contentContainer, />
contentContainerStyle, <View
desktopFixedHeight ? styles.minHeightViewport : null, style={[
pal.border, styles.contentContainer,
]}> contentContainerStyle,
<View style={styles.visibilityDetector}> desktopFixedHeight ? styles.minHeightViewport : null,
<Visibility onVisibleChange={setIsVisible} /> pal.border,
</View> ]}>
<View style={[styles.scrolledDownDetector, {height: headerOffset}]}> <Visibility
<Visibility onVisibleChange={handleAboveTheFoldVisibleChange}
onVisibleChange={handleScrolledDownDetectorVisibleChange} style={[styles.aboveTheFoldDetector, {height: headerOffset}]}
/> />
</View> {header}
{header} {(data as Array<ItemT>).map((item, index) => (
{(data as Array<ItemT>).map((item, index) => ( <Row<ItemT>
<Row<ItemT> key={keyExtractor!(item, index)}
key={keyExtractor!(item, index)} item={item}
item={item} index={index}
index={index} renderItem={renderItem}
renderItem={renderItem} extraData={extraData}
extraData={extraData} />
/> ))}
))} {onEndReached && (
{onEndReached && ( <Visibility
<Visibility topMargin={(onEndReachedThreshold ?? 0) * 100 + '%'}
topMargin={(onEndReachedThreshold ?? 0) * 100 + '%'} onVisibleChange={isVisible => {
onVisibleChange={isVisible => { if (isVisible) {
if (isVisible) { onEndReached?.({
onEndReached?.({ distanceFromEnd: onEndReachedThreshold || 0,
distanceFromEnd: onEndReachedThreshold || 0, })
}) }
} }}
}} />
/> )}
)} {footer}
{footer}
</View>
</View> </View>
</View> </View>
) )
@@ -199,9 +197,11 @@ Row = React.memo(Row)
let Visibility = ({ let Visibility = ({
topMargin = '0px', topMargin = '0px',
onVisibleChange, onVisibleChange,
style,
}: { }: {
topMargin?: string topMargin?: string
onVisibleChange: (isVisible: boolean) => void onVisibleChange: (isVisible: boolean) => void
style?: ViewProps['style']
}): React.ReactNode => { }): React.ReactNode => {
const tailRef = React.useRef(null) const tailRef = React.useRef(null)
const isIntersecting = React.useRef(false) const isIntersecting = React.useRef(false)
@@ -230,7 +230,9 @@ let Visibility = ({
} }
}, [handleIntersection, topMargin]) }, [handleIntersection, topMargin])
return <View ref={tailRef} /> return (
<View ref={tailRef} style={addStyle(styles.visibilityDetector, style)} />
)
} }
Visibility = React.memo(Visibility) Visibility = React.memo(Visibility)
@@ -257,14 +259,23 @@ const styles = StyleSheet.create({
// @ts-ignore web only // @ts-ignore web only
minHeight: '100vh', minHeight: '100vh',
}, },
visibilityDetector: { parentTreeVisibilityDetector: {
// @ts-ignore web only // @ts-ignore web only
position: 'fixed', position: 'fixed',
}, top: 0,
scrolledDownDetector: {
position: 'absolute',
left: 0, left: 0,
right: 0, right: 0,
bottom: 0,
},
aboveTheFoldDetector: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
// Bottom is dynamic.
},
visibilityDetector: {
pointerEvents: 'none', pointerEvents: 'none',
zIndex: -1,
}, },
}) })