0

I have the following pieces of code in my App.js:

  1. the ref for the main FlatList is set in a useEffect hook as flatListRef
const [flatListRef, setFlatListRef] = useState(null);
...
useEffect(() => {
    let mounted = true;
    if (refSlides?.current && mounted) setFlatListRef(refSlides.current);
    return () => mounted = false;
}, []);
  1. the renderItem function that renders the desired component as an item of the FlatList
const renderItem = ({item, index, separators}) => {
    return (
        <View style={{
            flexGrow: 1,
            width,
            height
        }}>
            {
                item.component === 'Welcome' && <Welcome/>
                || item.component === 'Hospital' &&
                <Hospital
                    flatListRef={flatListRef}
                    history={navHistory}
                    slides={slides}
                    setSlides={setSlides}
                    slide={item}/>
                || item.component === 'Snippets' &&
                <Snippets
                    flatListRef={flatListRef}
                    history={navHistory}
                    slides={slides}
                    setSlides={setSlides}
                    slide={item}
                />
                || item.component === 'Disease' &&
                <Disease
                    flatListRef={flatListRef}
                    history={navHistory}
                    slides={slides}
                    setSlides={setSlides}
                    slide={item}
                />
            }
        </View>
    );
};
  1. the actual FlatList definition
<FlatList
    data={slides}
    ref={refSlides}
    keyExtractor={item => item.key.toString()}
    renderItem={renderItem}
    getItemLayout={(data, index) => {
        return {
            length: width,
            offset: width * index,
            index
        };
    }}
    horizontal={true}
    showsHorizontalScrollIndicator={false}
    pagingEnabled={true}
/>

I have the following piece of code in any of my Welcome, Hospital, Snippets, Disease components. For example:

<TouchableOpacity
    activeOpacity={0.8}
    onPress={() => {
        setSlides([...slides, {
            key: uuidC.v4(),
            index: slides.length,
            uuid: item.uuid,
            component: 'Hospital'
        }]);
        flatListRef.scrollToIndex({index: slides.length - 1}); // <===== HERE IS THE PROBLEM
    }}>
    ...
</TouchableOpacity>

I have a main FlatList that has 2 components at the start, the Welcome component at index 0 and the Hospital component at index 1. In the Hospital component i have buttons, that when they are pressed, add another item (Hospital, Disease, Snippets) to the FlatList. The items are added successfully, i can manually slide to them.

The problem appears when i try to auto-scroll to the last item added. I want the following behaviour: after the button is pressed, a new item is added to the FlatList and auto-scrolls to it. Also i want to scroll at any index in the current FlatList.

The error i get is: scrollToIndex out of range: requested index 2 but maximum is 1.

Why does it say that maximum is 1 when i see with my eyes at least 3 items in the FlatList.

Have been at this for 2 days now, didn't find anything of use. Also i had not found examples where items where added or removed from FlatLists in conjunction with the scrollToIndex usage. Tried using VirtualizedLists, same problem as FlatList. I suspect it's a ref problem. I don't understand why the FlatList does not update the length of the data it renders, so i can scroll to it.

Any suggestions are appreciated.

Bogdan
  • 372
  • 3
  • 12
  • Is the height of each item the same? – Krismu Apr 08 '21 at 10:46
  • Yes, the FlatList is horizontal, so each item has full height and full width. – Bogdan Apr 08 '21 at 10:55
  • Initialize `flatListRef` with `refSlides.current` instead of `null`. – sandarshnaroju Apr 08 '21 at 11:49
  • @sandarshnaroju can't do that because at that point refSlides.current has no value. – Bogdan Apr 08 '21 at 12:00
  • `scrollToIndex` should be called like this `flatListRef.current.scrollToIndex({ index: slides.length - 1 });` and also, it is much safer to use ref to call scrollToIndex rather than a state. – sandarshnaroju Apr 09 '21 at 04:10
  • @Bogdan May I ask how you are defining `refSlides` and where? – sandarshnaroju Apr 09 '21 at 04:11
  • @sandarshnaroju i updated my post with `flatListRef.current.scrollToIndex({index: slides.length - 1})` because that's the way i was actually using it (still doesn't work). I define refSlides in App.js like `const refSlides = createRef()` and add it to the FlatList. – Bogdan Apr 09 '21 at 08:47
  • Define `refSlides` like this `const refSlides = useRef(null);` and then whenever u want to scroll use ` refSlides.current.scrollToIndex({index: slides.length - 1})`. createRef will give new Ref in each render, I think that is the problem. And also always use reference directly to call scrollToIndex rather than a state. – sandarshnaroju Apr 09 '21 at 10:05
  • @sandarshnaroju ok, so i've done the changes you suggested. I passed the refSlides directly to my button, i used `refSlides.current.scrollToIndex({index: slides.length - 1})` on button press, i don't get the error anymore, BUT i am "scrolled" to the same window i press the button in. As you can see in my code, just before the scrollToIndex call i call `setSlides` to add a new slide. The `slides.length` used in the scrollToIndex call does not take the new slide into consideration. – Bogdan Apr 09 '21 at 10:47
  • @Bogdan Ok `setState` take time to update its value, thats why `slides.length` is not updated after button press. How about you define another useRef which keeps track of slides data. Whenever you click button you update both ref and state with updated data. and in scrollToIndex use the slides data ref to scroll to the last item. – sandarshnaroju Apr 09 '21 at 11:01
  • @sandarshnaroju can you provide an example? Thanks. – Bogdan Apr 09 '21 at 12:15
  • @Bogdan Initialise like this, `const flatListRef = useRef(null); const slidesRef = useRef(null) //instead of null put the data of flatlist, I dont know where you are getting it maybe from api or as props, whatever it is put it here const [slides, setSlides] = useState(slidesRef.current);` for flatlist `` – sandarshnaroju Apr 09 '21 at 16:16
  • @Bogdan and In your onPress fucntion `slidesRef.current = [...slidesRef.current, { key: uuidC.v4(), index: slides.length, uuid: item.uuid, component: 'Hospital' }]; // here you have reference with updated flatlist data setSlides(slidesRef.current); flatListRef.current.scrollToIndex({index: slidesRef.current.length - 1})` – sandarshnaroju Apr 09 '21 at 16:17
  • @Bogdan Did you try it? Did it solve your problem? – sandarshnaroju Apr 14 '21 at 10:12

1 Answers1

0

Update flatListRef.scrollToIndex({index: I18nManager.isRTL ? array.length - index - 1 : index}); Where index is from Flatlist renderItem

Madhav Nasit
  • 101
  • 1
  • 3