Make isScrolledDown work

This commit is contained in:
Dan Abramov
2023-12-15 05:32:33 +00:00
parent 4939766676
commit af03785aaf
2 changed files with 41 additions and 20 deletions
-2
View File
@@ -39,9 +39,7 @@ function ListImpl<ItemT>(
const pal = usePalette('default') const pal = usePalette('default')
function handleScrolledDownChange(didScrollDown: boolean) { function handleScrolledDownChange(didScrollDown: boolean) {
startTransition(() => {
onScrolledDownChange?.(didScrollDown) onScrolledDownChange?.(didScrollDown)
})
} }
const scrollHandler = useAnimatedScrollHandler({ const scrollHandler = useAnimatedScrollHandler({
+35 -12
View File
@@ -1,9 +1,10 @@
import React, {memo, startTransition} from 'react' import React, {memo, useRef, startTransition} from 'react'
import {FlatListProps, StyleSheet, View} from 'react-native' import {FlatListProps, StyleSheet, View} 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'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {batchedUpdates} from '#/lib/batchedUpdates'
export type ListMethods = FlatList_INTERNAL export type ListMethods = FlatList_INTERNAL
export type ListProps<ItemT> = Omit< export type ListProps<ItemT> = Omit<
@@ -30,7 +31,7 @@ function ListImpl<ItemT>(
keyExtractor, keyExtractor,
refreshing: _unsupportedRefreshing, refreshing: _unsupportedRefreshing,
onEndReached, onEndReached,
onEndReachedThreshold, onEndReachedThreshold = 0,
onRefresh: _unsupportedOnRefresh, onRefresh: _unsupportedOnRefresh,
onScrolledDownChange, // TODO onScrolledDownChange, // TODO
renderItem, renderItem,
@@ -101,18 +102,28 @@ function ListImpl<ItemT>(
) )
const [isVisible, setIsVisible] = React.useState(false) const [isVisible, setIsVisible] = React.useState(false)
React.useEffect(() => { React.useEffect(() => {
if (!isVisible) { if (!isVisible) {
return return
} }
function handleScroll() { function handleScroll() {
console.log(window.scrollY) // console.log(window.scrollY)
} }
window.addEventListener('scroll', handleScroll) window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll) return () => window.removeEventListener('scroll', handleScroll)
}, [isVisible]) }, [isVisible])
const isScrolledDown = useRef(false)
function handleScrolledDownDetectorVisibleChange(isMarkerVisible: boolean) {
const didScrollDown = !isMarkerVisible
if (isScrolledDown.current !== didScrollDown) {
isScrolledDown.current = didScrollDown
startTransition(() => {
onScrolledDownChange?.(didScrollDown)
})
}
}
return ( return (
<View style={{paddingTop: 0}}> <View style={{paddingTop: 0}}>
<View {...props} style={style} ref={nativeRef}> <View {...props} style={style} ref={nativeRef}>
@@ -125,9 +136,13 @@ function ListImpl<ItemT>(
]}> ]}>
<View style={styles.visibilityDetector}> <View style={styles.visibilityDetector}>
<Visibility <Visibility
onVisibleChange={newIsVisible => { topMargin={(headerOffset ?? 0) + 'px'}
setIsVisible(newIsVisible) onVisibleChange={setIsVisible}
}} />
</View>
<View style={[styles.scrolledDownDetector, {height: headerOffset}]}>
<Visibility
onVisibleChange={handleScrolledDownDetectorVisibleChange}
/> />
</View> </View>
{header} {header}
@@ -142,7 +157,7 @@ function ListImpl<ItemT>(
))} ))}
{onEndReached && ( {onEndReached && (
<Visibility <Visibility
threshold={onEndReachedThreshold} topMargin={(onEndReachedThreshold ?? 0) * 100 + '%'}
onVisibleChange={isVisible => { onVisibleChange={isVisible => {
if (isVisible) { if (isVisible) {
onEndReached?.({ onEndReached?.({
@@ -185,10 +200,10 @@ let Row = function RowImpl<ItemT>({
Row = React.memo(Row) Row = React.memo(Row)
let Visibility = ({ let Visibility = ({
threshold = 0, topMargin = '0px',
onVisibleChange, onVisibleChange,
}: { }: {
threshold?: number | null | undefined topMargin?: string
onVisibleChange: (isVisible: boolean) => void onVisibleChange: (isVisible: boolean) => void
}): React.ReactNode => { }): React.ReactNode => {
const tailRef = React.useRef(null) const tailRef = React.useRef(null)
@@ -196,25 +211,27 @@ let Visibility = ({
const handleIntersection = useNonReactiveCallback( const handleIntersection = useNonReactiveCallback(
(entries: IntersectionObserverEntry[]) => { (entries: IntersectionObserverEntry[]) => {
batchedUpdates(() => {
entries.forEach(entry => { entries.forEach(entry => {
if (entry.isIntersecting !== isIntersecting.current) { if (entry.isIntersecting !== isIntersecting.current) {
isIntersecting.current = entry.isIntersecting isIntersecting.current = entry.isIntersecting
onVisibleChange(entry.isIntersecting) onVisibleChange(entry.isIntersecting)
} }
}) })
})
}, },
) )
React.useEffect(() => { React.useEffect(() => {
const observer = new IntersectionObserver(handleIntersection, { const observer = new IntersectionObserver(handleIntersection, {
rootMargin: (threshold || 0) * 100 + '%', rootMargin: `${topMargin} 0px 0px 0px`,
}) })
const tail: Element | null = tailRef.current! const tail: Element | null = tailRef.current!
observer.observe(tail) observer.observe(tail)
return () => { return () => {
observer.unobserve(tail) observer.unobserve(tail)
} }
}, [handleIntersection, threshold]) }, [handleIntersection, topMargin])
return <View ref={tailRef} /> return <View ref={tailRef} />
} }
@@ -247,4 +264,10 @@ const styles = StyleSheet.create({
// @ts-ignore web only // @ts-ignore web only
position: 'fixed', position: 'fixed',
}, },
scrolledDownDetector: {
position: 'absolute',
left: 0,
right: 0,
pointerEvents: 'none',
},
}) })