How to make swipe function on listview (react-native)?

mgicrush picture mgicrush · May 23, 2016 · Viewed 10.4k times · Source

I have list of items. I am using ListView for it and I need to be able to delete any row by swiping left or right.

Where can I start from here?

Answer

Adam Terlson picture Adam Terlson · May 23, 2016

If you prefer, follow this guide which uses React Native Swipeout.

Otherwise, here's my SwipeList and SwipeListRow component. I partially use my library Cairn for styling, but it should be easily translated into a normal React Stylesheet if you care to do so:

SwipeList.js

import React from 'react';
import { Text, ListView } from 'react-native';
import styleContext from 'app/style';

const style = styleContext.extend({
    listViewSection: {
        paddingVertical: 10,
        paddingLeft: 15,
        backgroundColor: '$greyDefault'
    },

    'text.listViewSection': {
        color: '$greyMid',
        fontSize: 16,
        marginLeft: 5
    }
});

function SwipeList({ dataSource, renderRow }) {
    function renderSectionHeader(sectionData, sectionId) {
        return (
            <View {...style('listViewSection')}>
                <Text {...style('text.listViewSection')}>{sectionId.toUpperCase()}</Text>
            </View>
        );
    }

    if (!dataSource.rowIdentities.length) {
        return (
            <Text>No items found.</Text>
        );
    }

    return (
        <ListView
            dataSource={dataSource}
            automaticallyAdjustContentInsets={false}
            directionalLockEnabled
            keyboardShouldPersistTaps={false}
            keyboardDismissMode={'on-drag'}
            renderSectionHeader={renderSectionHeader}
            renderRow={renderRow} />
    );
}

SwipeList.propTypes = {
    dataSource: React.PropTypes.shape({
        rowIdentities: React.PropTypes.array.isRequired
    }).isRequired,
    renderRow: React.PropTypes.func.isRequired
};

export default SwipeList;

SwipeListRow.js

import React from 'react';
import {
    View,
    Text,
    ScrollView,
    Animated,
    Dimensions
} from 'react-native';

import styleContext from 'app/style';

const { width } = Dimensions.get('window');
const style = styleContext.extend({
    swipeMessage: {
        position: 'absolute',
        top: 0,
        height: 75,
        justifyContent: 'center',
        alignItems: 'center'
    },

    itemContainer: {
        width
    }
});

const WHITE = 0;
const GREEN = 1;
const AnimatedScrollView = Animated.createAnimatedComponent(ScrollView);

class SwipeListRow extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            color: new Animated.Value(WHITE)
        };
    }

    animateScroll = (e) => {
        const threshold = width / 5;
        let x = e.nativeEvent.contentOffset.x;
        let swiped = null;

        x = x * -1;

        if (x > -50 && this.swiped !== WHITE) {
            swiped = WHITE;
        } else if (x < -50 && x > -threshold && this.swiped !== GREEN) {
            swiped = GREEN;
        }

        if (swiped !== null) {
            this.swiped = swiped;

            Animated.timing(this.state.color, {
                toValue: swiped,
                duration: 200
            }).start();
        }
    }

    takeAction = () => {
        if (this.swiped) {
            Animated.timing(this.state.color, {
                toValue: WHITE,
                duration: 200
            }).start();

            this.props.onSwipe();
        }
    }

    render() {
        const { swipeEnabled, swipeMessage, children } = this.props;
        const bgColor = this.state.color.interpolate({
            inputRange: [
                WHITE,
                GREEN
            ],
            outputRange: [
                'rgb(255, 255, 255)',
                'rgb(123, 204, 40)'
            ]
        });

        return (
            <View>
                <AnimatedScrollView
                    horizontal
                    directionalLockEnabled
                    automaticallyAdjustContentInsets={false}
                    onScroll={this.animateScroll}
                    scrollEventThrottle={16}
                    scrollEnabled={swipeEnabled}
                    onMomentumScrollBegin={this.takeAction}
                    style={[{ flex: 1 }, { backgroundColor: bgColor }]}>
                    <View>
                        <View {...style('itemContainer')}>
                            {children}
                        </View>
                        <View
                            {...style(
                                'swipeMessage',
                                [{ width: width / 5, right: -width / 5 - 20 }]
                            )}>
                            <Text {...style('text.bold text.white')}>{swipeMessage}</Text>
                        </View>
                    </View>
                </AnimatedScrollView>
            </View>
        );
    }
}

SwipeListRow.propTypes = {
    children: React.PropTypes.node.isRequired,
    onSwipe: React.PropTypes.func.isRequired,
    swipeEnabled: React.PropTypes.bool.isRequired,
    swipeMessage: React.PropTypes.string.isRequired
};


export default SwipeListRow;

With these components, now all you must do is pass in the required datasource as you would to a normal list view, as described on the ListView component documentation.

    <SwipeList
        dataSource={dataSource}
        renderRow={(item) => (
            <SwipeListRow
                key={item.id}
                swipeMessage={'Delete Item'}
                onSwipe={() => deleteItem(item)}
                swipeEnabled={true}>
                <<< INSERT DISPLAY OF ROW HERE >>>
            </SwipeListRow>
        )} />