Search code examples
react-nativerefflatlist

React-Native: Ref is missing scrollToIndex() for FlatList


I am passing a ref to a FlatList and expecting to access the scrollToIndex() function, but it doesn't appear in the console and it throws an error when I try to use it saying "scrollTo() is not a function", it doesn't even recognise that I'm trying to use scrollToIndex().

This is a simple example of usage in my code:

const ref = useRef()

ref.current?.scrollToIndex({index:itemIndex,animated:true})

<FlatList ref={ref} .../>

console.log for ref

Has anyone experienced this and knows how to make it work? I've looked over the documentation and examples and everything seems to be fine. Any help would be appreciated!

UPDATE:

Component:

import React from "react";
import { View, FlatList, ImageBackground,TouchableOpacity,ScrollView,StyleSheet, Text} from "react-native";


const Projects = ({ index, navigation }) => {
 const {width} = Dimensions.get("window")
  const [imageIndex, setIndex] = React.useState(0)
  const ref = React.useRef()
  const wItem = (width - 63) / 2;
console.log("index projects", imageIndex)


const scrollToIndex = (index)=>{
  if(ref.current){
    ref.current.scrollToIndex({index:index})
  }
}
 
const goToIndex = React.useCallback((info) => {
  const wait = new Promise(resolve => setTimeout(resolve, 200));
  wait.then(() => {
    ref.current?.scrollToIndex({ index: info.index, animated: true });
  })
}, [])

  return (
  
      <ScrollView >
      <TouchableOpacity onPress={()=> scrollToIndex(5)}><Text>Scroll to</Text></TouchableOpacity>
      <FlatList ref={ref}
      nestedScrollEnabled
        numColumns={2}
        data={DATA}
        onScrollToIndexFailed={(index)=>goToIndex(index)}
        style={styles.flatList}
        keyExtractor={(i, index) => index.toString()}
        renderItem={({ item, index }) => (
          <View style={styles.item}>
            <ImageBackground
              source={item.image}
              /* @ts-ignore */
              imageStyle={styles.imgStyle}
              style={{ width: wItem, height: wItem, alignItems: "flex-end" }}>
              
            </ImageBackground>
            </TouchableOpacity>
            <Text>{item.title}</Text>
          </View>
        )}
      />
    </ScrollView>
  
  );
};

export default Projects;

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  item: {
    marginRight: 15,
    marginBottom: 24,
    alignItems:'center'
   
  },
  flatList: {
    marginHorizontal: 24,
    marginTop: 16,
  },
  imgStyle: {
    borderRadius: 12,
  },
});

const DATA = [//list of local images]


Solution

  • It is unable to call scrollTo because the ref has only been initialized and not assigned at the time of use:

    const ref = useRef()
    // ref = null
    
    ref.current?.scrollToIndex({index:itemIndex,animated:true})
    // ref = null    
    
    <FlatList ref={ref} .../>
    // ref != null
    

    So ref is not assigned until you render for the first time. This means that if you want to call .scrollTo() on the ref you can do it in 2 different ways:

    1 - If you want to scroll to a certain index initially:

    useEffect(() => {
      const initialIndex = 34 //random number
      if (ref.current){
        ref.current.scrollToIndex({index: initialIndex})    
      }
    }, [ref])
    

    The ref in the dependency array will make sure that the useEffect will run whenever the value of ref changes (in this case it will change once it is assigned to a value by rendering the <Flatlist />

    2 - If you want to scroll to a certain index on demand:

    const handleScrollTo = (index) => {
      if (ref.current && typeof ref.current.scrollToIndex === 'function'){
        ref.current.scrollToIndex({index: index})    
      }
    }
    

    This function should be connected to something like an onPress or similar, which means the ref should be defined whenever the user presses the button because being able to see the button and press it requires that the component has rendered. Even though ref.current should already be assigned once it is used here, it is always good practice to make sure that it doesn't crash in cases where it is not, which is why we are adding the if-statement.