jemise111 / react-native-swipe-list-view

A React Native ListView component with rows that swipe open and closed
https://www.npmjs.com/package/react-native-swipe-list-view
MIT License
2.79k stars 527 forks source link

How to preserve list scroll position #471

Closed ccannell closed 4 years ago

ccannell commented 4 years ago

First off. Great library. Thank you for taking the time to develop and maintain it!

When I change the state with useState the component renders again and the list looses its scrolled position. Is there a way to preserve its scroll position?

jemise111 commented 4 years ago

Hey @ccannell I can almost guarantee this isn't an issue with this library. If you look at the examples in this repo you'll notice I use the useState hook to update state when a row is deleted and it definitely does not change the scroll position whatsoever.

e.g. https://github.com/jemise111/react-native-swipe-list-view/blob/master/SwipeListExample/examples/basic.js#L13

If you want to post more code here I'd be happy to try and help diagnose your issue though

ccannell commented 4 years ago

Here is the code in question.

Note. I'm using Redux connect as well.

I am trying to implement a scroll to top button that only appears when the list view is scrolled away from the top. I call setIsScrolling when the list view scrolls and then show a scroll to top button.

jemise111 commented 4 years ago

Hey @ccannell very strange that you're seeing this behavior. I set up an example as close to your component as possible and this is what I came up with and it seems to be working just fine.

I'm not sure how to help from here.. maybe look through and try to find differences in your code and this code below. Maybe show me a recording and that might help..?

SwipeScrollTop MP4

Here is the code for this example:

import React, { useState } from 'react';
import {
  StyleSheet,
  Text,
  TouchableOpacity,
  TouchableHighlight,
  View,
} from 'react-native';

import SwipeListView from '../SwipeListView';

let listViewRef = null;

export default function Basic() {
  const [listData, setListData] = useState(
    Array(20)
      .fill('')
      .map((_, i) => ({ key: `${i}`, text: `item #${i}` }))
  );

  const [isScrolling, setIsScrolling] = useState(false);

  const closeRow = (rowMap, rowKey) => {
    if (rowMap[rowKey]) {
      rowMap[rowKey].closeRow();
    }
  };

  const deleteRow = (rowMap, rowKey) => {
    closeRow(rowMap, rowKey);
    const newData = [...listData];
    const prevIndex = listData.findIndex(item => item.key === rowKey);
    newData.splice(prevIndex, 1);
    setListData(newData);
  };

  const renderItem = data => (
    <TouchableHighlight
      onPress={() => console.log('You touched me')}
      style={styles.rowFront}
      underlayColor={'#AAA'}
    >
      <View>
        <Text>I am {data.item.text} in a SwipeListView</Text>
      </View>
    </TouchableHighlight>
  );

  const renderHiddenItem = (data, rowMap) => (
    <View style={styles.rowBack}>
      <Text>Left</Text>
      <TouchableOpacity
        style={[styles.backRightBtn, styles.backRightBtnLeft]}
        onPress={() => closeRow(rowMap, data.item.key)}
      >
        <Text style={styles.backTextWhite}>Close</Text>
      </TouchableOpacity>
      <TouchableOpacity
        style={[styles.backRightBtn, styles.backRightBtnRight]}
        onPress={() => deleteRow(rowMap, data.item.key)}
      >
        <Text style={styles.backTextWhite}>Delete</Text>
      </TouchableOpacity>
    </View>
  );

  return (
    <View style={styles.container}>
      <SwipeListView
        listViewRef={ref => (listViewRef = ref)}
        data={listData}
        renderItem={renderItem}
        renderHiddenItem={renderHiddenItem}
        leftOpenValue={75}
        rightOpenValue={-150}
        previewRowKey={'0'}
        previewOpenValue={-40}
        previewOpenDelay={3000}
        onScroll={e => {
          setIsScrolling(e.nativeEvent.contentOffset.y > 0);
        }}
        onScrollToTop={() => setIsScrolling(false)}
        scrollEventThrottle={1}
      />
      {isScrolling && (
        <TouchableOpacity
          onPress={() =>
            listViewRef.scrollToOffset({
              x: 0,
              y: 0,
              animated: true,
            })
          }
          style={{
            alignItems: 'center',
            justifyContent: 'center',
            height: 40,
            width: 200,
            position: 'absolute',
            bottom: 30,
            right: 30,
            backgroundColor: 'red',
          }}
        >
          <Text style={{ color: 'white' }}>TOP</Text>
        </TouchableOpacity>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: 'white',
    flex: 1,
  },
  backTextWhite: {
    color: '#FFF',
  },
  rowFront: {
    alignItems: 'center',
    backgroundColor: '#CCC',
    borderBottomColor: 'black',
    borderBottomWidth: 1,
    justifyContent: 'center',
    height: 50,
  },
  rowBack: {
    alignItems: 'center',
    backgroundColor: '#DDD',
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'space-between',
    paddingLeft: 15,
  },
  backRightBtn: {
    alignItems: 'center',
    bottom: 0,
    justifyContent: 'center',
    position: 'absolute',
    top: 0,
    width: 75,
  },
  backRightBtnLeft: {
    backgroundColor: 'blue',
    right: 75,
  },
  backRightBtnRight: {
    backgroundColor: 'red',
    right: 0,
  },
});
jemise111 commented 4 years ago

@ccannell Okay one other thing I did notice is that you have two different SwipeListViews depending on which tab you're on. Is it possible you're losing scroll position because the tabs are changing? Maybe something about the using the same ref to track both SwipeListViews is causing problems? Again, a recording of the issue might help

ccannell commented 4 years ago

@jemise111 Thanks for the example. I'm going to do some more investigating.

ccannell commented 4 years ago

@jemise111 Thank you for the help. I tested again and it IS working. I'm using expo and hot reloading. I suspect the app was in a weird state. When I ran it again fresh it worked as it should. Thank you very much for the help.

jemise111 commented 4 years ago

Ah gotcha, yeah I find HMR Is really great for tweaking styles. But for changes in functionality it can get wonky. Glad it's working now.

Also @ccannell complete side note but after you linked your codebase I was looking around the Connect Our Kids website and noticed there's a section for Technology Volunteers. If you have the time I'd love to hear more about that if you're looking for more volunteers. My email is jesse.sessler@gmail.com, thanks!