Skip to content

ScrollToIndex doesn't seem to work when screen is out of focus. #1509

Open
@cSieves

Description

@cSieves

Calling scrollToIndex on flashlist when flashlist is out of focus doesn't seem to work.

I have a page like this ;

const HomePage = () => {
    const dispatch = useDispatch();
    const route = useRoute()
    const flashListRef = useRef(null);
    const scrollPosition = useRef(0)
    const navigation = useNavigation();

    const [refreshing, setRefreshing] = useState(false);

    const shouldRender = useNavigationState((state) => state.routes.length < 3 || state.routes.slice(-3).some(r => r.key === route.key));;

    const {user} = useSelector((state) => state.auth);
    const {images, isLoading, hasMore, refreshLoading} = useSelector((state) => state.images);


    useEffect(() => {
        dispatch(fetchImages({lastImageId: null, isAppending: false}));
    }, []);


    const handleScroll = (event) => {
        scrollPosition.current = event.nativeEvent.contentOffset.y;  // Get current offset
    };

    const renderItem = useCallback(
        ({item}) => <TestComponent item={item}/>
        []
    );

    useEffect(() => {
        if(shouldRender){
            if (flashListRef.current && scrollPosition.current !== 0) {
                try {
                    flashListRef.current.scrollToOffset({
                        offset: scrollPosition.current, // Use the saved offset
                        animated: false, // You can set this to true if you want an animated scroll
                    });
                } catch (e) {
                    console.error(e)
                }
            }
        }
    }, [shouldRender]);

    if (!shouldRender) {
        return null;
    }

    return (
        <View style={styles.container}>
                <FlashList
                    ref={flashListRef}
                    data={images}
                    keyExtractor={item => item.id.toString()}
                    onScroll={handleScroll}
                    scrollEventThrottle={16}
                    renderItem={renderItem}
                />
            )}
        </View>
    );
};

The idea behind this component is that we keep track of the users scrolling on the page and when they leave we save it. As my app supports almost infinite navigation, similar to how a social media app may allow you to navigate from profile to profile, I need a way to optimise the page rendering so I don't render every page and run out of memory. My workaround for this is to only ever render the previous previous three pages and each page listens to the navigation state to see if its in the last three pages. If it is the component renders so that it is ready for the user, if not it returns null, conserving memory. This is what the shouldRender variable does here.

This works with flatlist, however, when doing this with flashlist the page always re-renders with the scroll position at zero, even though the scrollToIndex does execute.

Refactoring my code to only scroll when the page is in focus, works.

const HomePage = () => {
    const dispatch = useDispatch();
    const route = useRoute()
    const flashListRef = useRef(null);
    const scrollPosition = useRef(0)
    const navigation = useNavigation();

    const [refreshing, setRefreshing] = useState(false);

    const {user} = useSelector((state) => state.auth);
    const {images, isLoading, hasMore, refreshLoading} = useSelector((state) => state.images);


    useEffect(() => {
        dispatch(fetchImages({lastImageId: null, isAppending: false}));
    }, []);


    const handleScroll = (event) => {
        scrollPosition.current = event.nativeEvent.contentOffset.y;  // Get current offset
    };

    const renderItem = useCallback(
        ({item}) => <TestComponent item={item}/>
        []
    );

   const isFocused = useIsFocused();
    useEffect(() => {
        if(isFocused){
            if (flashListRef.current && scrollPosition.current !== 0) {
                try {
                    flashListRef.current.scrollToOffset({
                        offset: scrollPosition.current, // Use the saved offset
                        animated: false, // You can set this to true if you want an animated scroll
                    });
                } catch (e) {
                    console.error(e)
                }
            }
        }
    }, [isFocused]);

    if (!isFocused) {
        return null;
    }

    return (
        <View style={styles.container}>
                <FlashList
                    ref={flashListRef}
                    data={images}
                    keyExtractor={item => item.id.toString()}
                    onScroll={handleScroll}
                    scrollEventThrottle={16}
                    renderItem={renderItem}
                />
            )}
        </View>
    );
};

However, my concern here is that the scroll won't happen on time on all devices so the user will see the page jump. This seems like a bug to me but maybe it is intentional.

Current behavior

Calling scrollToIndex when page is out of focus does not work

Expected behavior

Calling scrollToIndex when page is out of focus should work

To Reproduce

See Sample code above

Platform:

  • iOS
  • Android

Environment

1.7.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions