Address lint warnings in List.web.tsx (#10046)

This commit is contained in:
DS Boyce
2026-03-13 09:41:38 -07:00
committed by GitHub
parent 97fdd7c59b
commit b8d60eb0e2
+45 -33
View File
@@ -1,9 +1,13 @@
import React, { import {
forwardRef,
isValidElement, isValidElement,
type JSX,
memo, memo,
startTransition, startTransition,
useCallback,
useEffect,
useImperativeHandle,
useRef, useRef,
useState,
} from 'react' } from 'react'
import { import {
type FlatListProps, type FlatListProps,
@@ -39,7 +43,7 @@ export type ListProps<ItemT> = Omit<
*/ */
sideBorders?: boolean sideBorders?: boolean
} }
export type ListRef = React.MutableRefObject<any | null> // TODO: Better types. export type ListRef = React.RefObject<View>
const ON_ITEM_SEEN_WAIT_DURATION = 0.5e3 // when we consider post to be "seen" const ON_ITEM_SEEN_WAIT_DURATION = 0.5e3 // when we consider post to be "seen"
const ON_ITEM_SEEN_INTERSECTION_OPTS = { const ON_ITEM_SEEN_INTERSECTION_OPTS = {
@@ -77,7 +81,7 @@ function ListImpl<ItemT>(
const isEmpty = !data || data.length === 0 const isEmpty = !data || data.length === 0
let headerComponent: JSX.Element | null = null let headerComponent: React.JSX.Element | null = null
if (ListHeaderComponent != null) { if (ListHeaderComponent != null) {
if (isValidElement(ListHeaderComponent)) { if (isValidElement(ListHeaderComponent)) {
headerComponent = ListHeaderComponent headerComponent = ListHeaderComponent
@@ -87,7 +91,7 @@ function ListImpl<ItemT>(
} }
} }
let footerComponent: JSX.Element | null = null let footerComponent: React.JSX.Element | null = null
if (ListFooterComponent != null) { if (ListFooterComponent != null) {
if (isValidElement(ListFooterComponent)) { if (isValidElement(ListFooterComponent)) {
footerComponent = ListFooterComponent footerComponent = ListFooterComponent
@@ -97,7 +101,7 @@ function ListImpl<ItemT>(
} }
} }
let emptyComponent: JSX.Element | null = null let emptyComponent: React.JSX.Element | null = null
if (ListEmptyComponent != null) { if (ListEmptyComponent != null) {
if (isValidElement(ListEmptyComponent)) { if (isValidElement(ListEmptyComponent)) {
emptyComponent = ListEmptyComponent emptyComponent = ListEmptyComponent
@@ -113,9 +117,9 @@ function ListImpl<ItemT>(
}) })
} }
const getScrollableNode = React.useCallback(() => { const getScrollableNode = useCallback(() => {
if (disableFullWindowScroll) { if (disableFullWindowScroll) {
const element = nativeRef.current as HTMLDivElement | null const element = nativeRef.current
if (!element) return if (!element) return
return { return {
@@ -186,8 +190,8 @@ function ListImpl<ItemT>(
} }
}, [disableFullWindowScroll]) }, [disableFullWindowScroll])
const nativeRef = React.useRef<HTMLDivElement>(null) const nativeRef = useRef<HTMLDivElement>(null)
React.useImperativeHandle( useImperativeHandle(
ref, ref,
() => () =>
({ ({
@@ -226,7 +230,7 @@ function ListImpl<ItemT>(
useResizeObserver(containerRef, onContentSizeChange) useResizeObserver(containerRef, onContentSizeChange)
// --- onScroll --- // --- onScroll ---
const [isInsideVisibleTree, setIsInsideVisibleTree] = React.useState(false) const [isInsideVisibleTree, setIsInsideVisibleTree] = useState(false)
const handleScroll = useNonReactiveCallback(() => { const handleScroll = useNonReactiveCallback(() => {
if (!isInsideVisibleTree) return if (!isInsideVisibleTree) return
@@ -257,7 +261,7 @@ function ListImpl<ItemT>(
) )
}) })
React.useEffect(() => { useEffect(() => {
if (!isInsideVisibleTree) { if (!isInsideVisibleTree) {
// Prevents hidden tabs from firing scroll events. // Prevents hidden tabs from firing scroll events.
// Only one list is expected to be firing these at a time. // Only one list is expected to be firing these at a time.
@@ -395,7 +399,7 @@ function EdgeVisibility({
containerRef: React.RefObject<Element | null> containerRef: React.RefObject<Element | null>
onVisibleChange: (isVisible: boolean) => void onVisibleChange: (isVisible: boolean) => void
}) { }) {
const [containerHeight, setContainerHeight] = React.useState(0) const [containerHeight, setContainerHeight] = useState(0)
useResizeObserver(containerRef, (w, h) => { useResizeObserver(containerRef, (w, h) => {
setContainerHeight(h) setContainerHeight(h)
}) })
@@ -416,7 +420,7 @@ function useResizeObserver(
) { ) {
const handleResize = useNonReactiveCallback(onResize ?? (() => {})) const handleResize = useNonReactiveCallback(onResize ?? (() => {}))
const isActive = !!onResize const isActive = !!onResize
React.useEffect(() => { useEffect(() => {
if (!isActive) { if (!isActive) {
return return
} }
@@ -452,10 +456,10 @@ let Row = function RowImpl<ItemT>({
extraData: any extraData: any
onItemSeen: ((item: any) => void) | undefined onItemSeen: ((item: any) => void) | undefined
}): React.ReactNode { }): React.ReactNode {
const rowRef = React.useRef(null) const rowRef = useRef(null)
const intersectionTimeout = React.useRef< const intersectionTimeout = useRef<ReturnType<typeof setTimeout> | undefined>(
ReturnType<typeof setTimeout> | undefined undefined,
>(undefined) )
const handleIntersection = useNonReactiveCallback( const handleIntersection = useNonReactiveCallback(
(entries: IntersectionObserverEntry[]) => { (entries: IntersectionObserverEntry[]) => {
@@ -468,12 +472,12 @@ let Row = function RowImpl<ItemT>({
if (!intersectionTimeout.current) { if (!intersectionTimeout.current) {
intersectionTimeout.current = setTimeout(() => { intersectionTimeout.current = setTimeout(() => {
intersectionTimeout.current = undefined intersectionTimeout.current = undefined
onItemSeen!(item) onItemSeen(item)
}, ON_ITEM_SEEN_WAIT_DURATION) }, ON_ITEM_SEEN_WAIT_DURATION)
} }
} else { } else {
if (intersectionTimeout.current) { if (intersectionTimeout.current) {
clearTimeout(intersectionTimeout.current as NodeJS.Timeout) clearTimeout(intersectionTimeout.current)
intersectionTimeout.current = undefined intersectionTimeout.current = undefined
} }
} }
@@ -482,7 +486,7 @@ let Row = function RowImpl<ItemT>({
}, },
) )
React.useEffect(() => { useEffect(() => {
if (!onItemSeen) { if (!onItemSeen) {
return return
} }
@@ -490,10 +494,14 @@ let Row = function RowImpl<ItemT>({
handleIntersection, handleIntersection,
ON_ITEM_SEEN_INTERSECTION_OPTS, ON_ITEM_SEEN_INTERSECTION_OPTS,
) )
const row: Element | null = rowRef.current! const row: Element | null = rowRef.current
observer.observe(row) if (row) {
observer.observe(row)
}
return () => { return () => {
observer.unobserve(row) if (row) {
observer.unobserve(row)
}
} }
}, [handleIntersection, onItemSeen]) }, [handleIntersection, onItemSeen])
@@ -507,7 +515,7 @@ let Row = function RowImpl<ItemT>({
</View> </View>
) )
} }
Row = React.memo(Row) Row = memo(Row)
let Visibility = ({ let Visibility = ({
root, root,
@@ -522,8 +530,8 @@ let Visibility = ({
onVisibleChange: (isVisible: boolean) => void onVisibleChange: (isVisible: boolean) => void
style?: ViewProps['style'] style?: ViewProps['style']
}): React.ReactNode => { }): React.ReactNode => {
const tailRef = React.useRef(null) const tailRef = useRef(null)
const isIntersecting = React.useRef(false) const isIntersecting = useRef(false)
const handleIntersection = useNonReactiveCallback( const handleIntersection = useNonReactiveCallback(
(entries: IntersectionObserverEntry[]) => { (entries: IntersectionObserverEntry[]) => {
@@ -538,15 +546,19 @@ let Visibility = ({
}, },
) )
React.useEffect(() => { useEffect(() => {
const observer = new IntersectionObserver(handleIntersection, { const observer = new IntersectionObserver(handleIntersection, {
root: root?.current ?? null, root: root?.current ?? null,
rootMargin: `${topMargin} 0px ${bottomMargin} 0px`, rootMargin: `${topMargin} 0px ${bottomMargin} 0px`,
}) })
const tail: Element | null = tailRef.current! const tail: Element | null = tailRef.current
observer.observe(tail) if (tail) {
observer.observe(tail)
}
return () => { return () => {
observer.unobserve(tail) if (tail) {
observer.unobserve(tail)
}
} }
}, [bottomMargin, handleIntersection, topMargin, root]) }, [bottomMargin, handleIntersection, topMargin, root])
@@ -554,9 +566,9 @@ let Visibility = ({
<View ref={tailRef} style={addStyle(styles.visibilityDetector, style)} /> <View ref={tailRef} style={addStyle(styles.visibilityDetector, style)} />
) )
} }
Visibility = React.memo(Visibility) Visibility = memo(Visibility)
export const List = memo(React.forwardRef(ListImpl)) as <ItemT>( export const List = memo(forwardRef(ListImpl)) as <ItemT>(
props: ListProps<ItemT> & {ref?: React.Ref<ListMethods>}, props: ListProps<ItemT> & {ref?: React.Ref<ListMethods>},
) => React.ReactElement<any> ) => React.ReactElement<any>