From b8d60eb0e29b9167fdfb7350912e6d5804e9a037 Mon Sep 17 00:00:00 2001 From: DS Boyce <260543580+ds-boyce@users.noreply.github.com> Date: Fri, 13 Mar 2026 09:41:38 -0700 Subject: [PATCH] Address lint warnings in List.web.tsx (#10046) --- src/view/com/util/List.web.tsx | 78 ++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/src/view/com/util/List.web.tsx b/src/view/com/util/List.web.tsx index 584738eea9..0c9669edcc 100644 --- a/src/view/com/util/List.web.tsx +++ b/src/view/com/util/List.web.tsx @@ -1,9 +1,13 @@ -import React, { +import { + forwardRef, isValidElement, - type JSX, memo, startTransition, + useCallback, + useEffect, + useImperativeHandle, useRef, + useState, } from 'react' import { type FlatListProps, @@ -39,7 +43,7 @@ export type ListProps = Omit< */ sideBorders?: boolean } -export type ListRef = React.MutableRefObject // TODO: Better types. +export type ListRef = React.RefObject const ON_ITEM_SEEN_WAIT_DURATION = 0.5e3 // when we consider post to be "seen" const ON_ITEM_SEEN_INTERSECTION_OPTS = { @@ -77,7 +81,7 @@ function ListImpl( const isEmpty = !data || data.length === 0 - let headerComponent: JSX.Element | null = null + let headerComponent: React.JSX.Element | null = null if (ListHeaderComponent != null) { if (isValidElement(ListHeaderComponent)) { headerComponent = ListHeaderComponent @@ -87,7 +91,7 @@ function ListImpl( } } - let footerComponent: JSX.Element | null = null + let footerComponent: React.JSX.Element | null = null if (ListFooterComponent != null) { if (isValidElement(ListFooterComponent)) { footerComponent = ListFooterComponent @@ -97,7 +101,7 @@ function ListImpl( } } - let emptyComponent: JSX.Element | null = null + let emptyComponent: React.JSX.Element | null = null if (ListEmptyComponent != null) { if (isValidElement(ListEmptyComponent)) { emptyComponent = ListEmptyComponent @@ -113,9 +117,9 @@ function ListImpl( }) } - const getScrollableNode = React.useCallback(() => { + const getScrollableNode = useCallback(() => { if (disableFullWindowScroll) { - const element = nativeRef.current as HTMLDivElement | null + const element = nativeRef.current if (!element) return return { @@ -186,8 +190,8 @@ function ListImpl( } }, [disableFullWindowScroll]) - const nativeRef = React.useRef(null) - React.useImperativeHandle( + const nativeRef = useRef(null) + useImperativeHandle( ref, () => ({ @@ -226,7 +230,7 @@ function ListImpl( useResizeObserver(containerRef, onContentSizeChange) // --- onScroll --- - const [isInsideVisibleTree, setIsInsideVisibleTree] = React.useState(false) + const [isInsideVisibleTree, setIsInsideVisibleTree] = useState(false) const handleScroll = useNonReactiveCallback(() => { if (!isInsideVisibleTree) return @@ -257,7 +261,7 @@ function ListImpl( ) }) - React.useEffect(() => { + useEffect(() => { if (!isInsideVisibleTree) { // Prevents hidden tabs from firing scroll events. // Only one list is expected to be firing these at a time. @@ -395,7 +399,7 @@ function EdgeVisibility({ containerRef: React.RefObject onVisibleChange: (isVisible: boolean) => void }) { - const [containerHeight, setContainerHeight] = React.useState(0) + const [containerHeight, setContainerHeight] = useState(0) useResizeObserver(containerRef, (w, h) => { setContainerHeight(h) }) @@ -416,7 +420,7 @@ function useResizeObserver( ) { const handleResize = useNonReactiveCallback(onResize ?? (() => {})) const isActive = !!onResize - React.useEffect(() => { + useEffect(() => { if (!isActive) { return } @@ -452,10 +456,10 @@ let Row = function RowImpl({ extraData: any onItemSeen: ((item: any) => void) | undefined }): React.ReactNode { - const rowRef = React.useRef(null) - const intersectionTimeout = React.useRef< - ReturnType | undefined - >(undefined) + const rowRef = useRef(null) + const intersectionTimeout = useRef | undefined>( + undefined, + ) const handleIntersection = useNonReactiveCallback( (entries: IntersectionObserverEntry[]) => { @@ -468,12 +472,12 @@ let Row = function RowImpl({ if (!intersectionTimeout.current) { intersectionTimeout.current = setTimeout(() => { intersectionTimeout.current = undefined - onItemSeen!(item) + onItemSeen(item) }, ON_ITEM_SEEN_WAIT_DURATION) } } else { if (intersectionTimeout.current) { - clearTimeout(intersectionTimeout.current as NodeJS.Timeout) + clearTimeout(intersectionTimeout.current) intersectionTimeout.current = undefined } } @@ -482,7 +486,7 @@ let Row = function RowImpl({ }, ) - React.useEffect(() => { + useEffect(() => { if (!onItemSeen) { return } @@ -490,10 +494,14 @@ let Row = function RowImpl({ handleIntersection, ON_ITEM_SEEN_INTERSECTION_OPTS, ) - const row: Element | null = rowRef.current! - observer.observe(row) + const row: Element | null = rowRef.current + if (row) { + observer.observe(row) + } return () => { - observer.unobserve(row) + if (row) { + observer.unobserve(row) + } } }, [handleIntersection, onItemSeen]) @@ -507,7 +515,7 @@ let Row = function RowImpl({ ) } -Row = React.memo(Row) +Row = memo(Row) let Visibility = ({ root, @@ -522,8 +530,8 @@ let Visibility = ({ onVisibleChange: (isVisible: boolean) => void style?: ViewProps['style'] }): React.ReactNode => { - const tailRef = React.useRef(null) - const isIntersecting = React.useRef(false) + const tailRef = useRef(null) + const isIntersecting = useRef(false) const handleIntersection = useNonReactiveCallback( (entries: IntersectionObserverEntry[]) => { @@ -538,15 +546,19 @@ let Visibility = ({ }, ) - React.useEffect(() => { + useEffect(() => { const observer = new IntersectionObserver(handleIntersection, { root: root?.current ?? null, rootMargin: `${topMargin} 0px ${bottomMargin} 0px`, }) - const tail: Element | null = tailRef.current! - observer.observe(tail) + const tail: Element | null = tailRef.current + if (tail) { + observer.observe(tail) + } return () => { - observer.unobserve(tail) + if (tail) { + observer.unobserve(tail) + } } }, [bottomMargin, handleIntersection, topMargin, root]) @@ -554,9 +566,9 @@ let Visibility = ({ ) } -Visibility = React.memo(Visibility) +Visibility = memo(Visibility) -export const List = memo(React.forwardRef(ListImpl)) as ( +export const List = memo(forwardRef(ListImpl)) as ( props: ListProps & {ref?: React.Ref}, ) => React.ReactElement