3

I got a component that I want to make some sort like filter page at side something looks like this


A

B

C

D

E

F

G

H

.

.

.


once the user scroll until Z, the user can see A back after Z.. it's endless loop using flatList. How do i archieve that ?


Y

Z

A

B

C

D

.

.

.


right now all i test using onEndReached I move back to initial position but it not a good experience. anyone can give a bit tips or guide?

Thanks!

_renderItem = ({item, index}) => (
    <Text>{item}</Text>
);

<FlatList
  data={['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']}
  keyExtractor={this._keyExtractor}
  renderItem={this._renderItem}
  showsVerticalScrollIndicator={false}
/>
Hazim Ali
  • 1,077
  • 4
  • 17
  • 28
  • 1
    Hey, please check this answer. It looks exactly like what you're looking for. https://stackoverflow.com/a/49338832/9890122 – Harrison Sep 27 '18 at 19:29

2 Answers2

1

you can use the package: react-native-infinite-looping-scroll from https://github.com/prateek3255/react-native-infinite-looping-scroll. here's it's source code.

import React, { Component } from 'react';
import { FlatList } from 'react-native';
import PropTypes from 'prop-types'

export default class InfiniteScroll extends Component {
    constructor(props) {
        super(props)
        this.state = {
            data: this.props.data,
            end: true,
        }
        length = this.state.data.length
        data = this.state.data.slice()
    }
    checkScroll({ layoutMeasurement, contentOffset, contentSize }) {
        if (this.state.data.length >= length * 3)
            this.setState(prevState => ({
                data: prevState.data.slice(length * 2)
            }))

        if (contentOffset.y <= this.props.offset) {
            this.setState(prevState => ({
                data: [...prevState.data, ...data],
            }), () => this.infListRef.scrollToIndex({ index: length, animated: false }))
        }
        if (layoutMeasurement.height + contentOffset.y >= contentSize.height - this.props.offset && this.state.end) {
            this.setState(prevState => ({
                data: [...prevState.data, ...data],
                end: false
            }))
        }
        else {
            this.setState({
                end: true
            })
        }

    }
    componentDidMount() {
        this.setState(prevState => ({
            data: [...prevState.data, ...prevState.data]
        }))
        setTimeout(() => { this.infListRef.scrollToIndex({ animated: false, index: length }) }, 500);

    }
    render() {
        return (

            <FlatList
                {...this.props}
                ref={(ref) => { this.infListRef = ref; }}
                data={this.state.data}
                renderItem={this.props.renderItem}
                onScroll={({ nativeEvent }) => this.checkScroll(nativeEvent)}
                showsVerticalScrollIndicator={this.props.showsVerticalScrollIndicator}
            />
        );
    }
}

InfiniteScroll.propTypes = {
    offset: PropTypes.number,
    showsVerticalScrollIndicator: PropTypes.bool
}

InfiniteScroll.defaultProps = {
    offset: 20,
    showsVerticalScrollIndicator: false
};
LightMan
  • 3,517
  • 31
  • 31
Amir Gorji
  • 2,809
  • 1
  • 21
  • 26
0

The best solution without having to scroll to the center I found is using array like object with very big length and use it as data.

const loopedData = {length: 100000} as undefined[]; // created outside component

const initialIndex = originalData
    ? Math.round(loopedData.length / 2 - originalData.length / 2)
    : 0;

<FlatList
  ...
  data={originalData ? loopedData : null}
  extraData={originalData}
  initialScrollIndex={initialIndex}
  getItemLayout={getItemLayout}
  ...
/>

But in render take data from original array:


const render = (info: ListRenderItemInfo<undefined>) => {
  if (!originalData) return null;

  const originalItemIndex = getOriginalItemIndex(
    info.index,
    originalData.length,
    initialIndex,
  );

  return renderItemImpl?.({
    index: originalItemIndex,
    item: originalData[originalItemIndex],
    separators: info.separators,
  });
}

Here are some helpers for implementing this approach:

export const getOriginalItemIndex = (
  index: number,
  originalLength: number,
  initialScrollIndex: number,
) => {
  return (
    (index + originalLength - (initialScrollIndex % originalLength)) %
    originalLength
  );
};

// used for scrolling to nearest index
export const getNearestToCurrentIndex = (
  currentIndex: number,
  originalIndex: number,
  originalIndexTo: number,
  originalLength: number,
) => {
  if (originalIndex === originalIndexTo) return currentIndex;

  const rightDistance =
    originalIndexTo > originalIndex
      ? originalIndexTo - originalIndex
      : originalLength - originalIndex + originalIndexTo;
  const leftDistance = originalLength - rightDistance;
  const result =
    rightDistance <= leftDistance
      ? currentIndex + rightDistance
      : currentIndex - leftDistance;
  return result;
};

You can also recenter list when screen disappears but not unmounted, but not necessary.

Alexander Danilov
  • 3,038
  • 1
  • 30
  • 35