Closing a Swipeable element when another is opened

I’m currently using Swipeable to create a list of items, each of which the user can swipe to delete. It’s working fine but if I swipe a row and open one and then swipe another, the previously-opened row is still swiped open but I’d like it to be closed.

I’ve searched for solutions and made a little progress myself using refs but I’ve not been able to get it working perfectly. I’ve also come across quite a few people asking for the same thing and it seems like pretty standard behaviour. It’d be great if you could add some documentation with examples covering this.

Thank you.

9 thoughts on “Closing a Swipeable element when another is opened

  1. I tried using ref as well and it seems to be working fine except that the close animation is delayed a little.

    can you show us your workaround ?

    @AmanSharma2609 The idea is using ref and close(). Hope it helps

    let row: Array<any> = [];
    let prevOpenedRow;
    
    renderItem ({ item, index }) {
      return (
        <Swipeable
    	ref={ref => row[index] = ref}
    	friction={2}
    	leftThreshold={80}
    	rightThreshold={40}
    	renderRightActions={renderRightActions}
    	containerStyle={style.swipeRowStyle}
            onSwipeableOpen={closeRow(index)}
    	...
        >
        ...
        </Swipeable>);
    }
    
    closeRow(index) {
        if (prevOpenedRow && prevOpenedRow !== row[index]) {
    		prevOpenedRow.close();
        }
        prevOpenedRow = row[index];
    }
    
  2. i use array of ref to fixed this issue, i think this issue cause by single ref, when we iterate over array the ref became the last ref of item in list so i did give all the list of item a ref based on index or item.id and called the ref based on index or id

    first

    constructor(props) {
        super(props);
        this.state = {
        };
        this.refsArray = []; // add this
      }
    

    then in swipeable collect the ref by passing index or your custom id

    <Swipeable
            ref={ref => {
              this.refsArray[index] = ref; //or this.refsArray[item.id] 
            }} 
            friction={2}
            leftThreshold={30}
            rightThreshold={40}
            renderRightActions={(progress) => this.renderRightActions(progress, item)}
          >
    

    then to close one item do this in your method
    this.refsArray[index].close(); // or this.refsArray[item.id].close(); if you are using custom id

  3. I tried using ref as well and it seems to be working fine except that the close animation is delayed a little.

    can you show us your workaround ?

    @AmanSharma2609 The idea is using ref and close(). Hope it helps

    let row: Array<any> = [];
    let prevOpenedRow;
    
    renderItem ({ item, index }) {
      return (
        <Swipeable
    	ref={ref => row[index] = ref}
    	friction={2}
    	leftThreshold={80}
    	rightThreshold={40}
    	renderRightActions={renderRightActions}
    	containerStyle={style.swipeRowStyle}
            onSwipeableOpen={closeRow(index)}
    	...
        >
        ...
        </Swipeable>);
    }
    
    closeRow(index) {
        if (prevOpenedRow && prevOpenedRow !== row[index]) {
    		prevOpenedRow.close();
        }
        prevOpenedRow = row[index];
    }
    

    @anniewey It worked thank you!!

  4. I tried using ref as well and it seems to be working fine except that the close animation is delayed a little.

    This solution works for me. I’m using react-native-gesture-handler v1.4.1 and we have the method onSwipeableWillOpen(). Try to use this instead onSwipeableOpen().

  5. List.js
    let swipedCardRef = null;
      const onOpen = ref => {
        if (swipedCardRef) swipedCardRef.current.close();
        swipedCardRef = ref;
      };
      const onClose = ref => {
        if (ref == swipedCardRef) {
          swipedCardRef = null;
        }
      };
      const renderItem = ({ item, index }) => {
        return (
          <Item
            item={item}
            index={item}
            removeItem={removeItem(item)}
            onOpen={onOpen}
            onClose={onClose}
          />
        );
      };
    <Flatlist data={[...]} renderItem={renderItem} ... />
    
    Item.js
    const onSwipeOpen = () => {
        if (!isOpened) {
          isOpened = true;
          onOpen(rowRef);
        }
      };
      const onSwipeClose = () => {
        if (isOpened) {
          isOpened = false;
          onClose(rowRef);
        }
      };
      return (
        <Swipeable
          ref={rowRef}
          renderLeftActions={renderLeftActions}
          onSwipeableOpen={onSwipeOpen}
          onSwipeableClose={onSwipeClose}
        >
          <TouchableOpacity
            onPressIn={onSwipeOpen}
          >
            ...
          </TouchableOpacity>
        </Swipeable>
      );
    
    

    It can help you!

  6. This seems to be working for me. Sharing just in case

     let rowRefs = new Map();
      
      const renderItem = ({item}) => (
      <Swipeable
        key={item.key}
        ref={ref => {
          if (ref && !rowRefs.get(item.key)) {
            rowRefs.set(item.key, ref);
          }
        }}
        onSwipeableWillOpen={()=>{
            [...rowRefs.entries()].forEach(([key, ref]) => {
              if (key !== item.key && ref) ref.close();
            });
         }}
         >
       </Swipeable>
      );