React-native-gesture-handler: Closing a Swipeable element when another is opened

Created on 16 Sep 2019  路  22Comments  路  Source: software-mansion/react-native-gesture-handler

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.

Question Swipeable

Most helpful comment

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];
}

All 22 comments

any update? 馃槶

Need help on this too! I'm working on closing Swipeable by index instead of last swiped item.

Need help on this too! I'm working on closing Swipeable by index instead of last swiped item.

Yeah, I played around with a solution like that but nothing felt that good so, unfortunately, I removed the whole swiping gesture from my app.

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

Hi, can someone help me. T tried the example project but when i tried to close the swipeable view but its not working. Am I missing or doingsomething.. Any help is really appreciated. Thank you so much.

this is the sample project link : https://github.com/kmagiera/react-native-gesture-handler/blob/master/Example/swipeable/AppleStyleSwipeableRow.js

Any chance we can get an update on this? It seems like a few people are wondering the same thing. Thanks!

I would like to introduce this function i my apps

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 ?

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];
}

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

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!!

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()_.

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!

I wrapped my list in a SwipeProvider. It uses Context to maintain the state of which item is open. When an item opens, it updates the context with an id. All the other swipe-ables use a hook to see if the currently open id matches their id. If it doesn't, they close themselves.

@calendee i鈥檓 trying to do the same thing. What prop do you use to force close the non matching rows?

@Lsleiman Sorry for the slow response.

Here's a sample: https://gist.github.com/calendee/ba37861b237b57ee49b7949766c9a0da

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>
  );

rowRef

I'm getting rowRef is undefined

@ShravanMeena I used rowRefs in my example

@Lsleiman Sorry for the slow response.

Here's a sample: https://gist.github.com/calendee/ba37861b237b57ee49b7949766c9a0da

Thank you @calendee . Your solution worked for me. I had three tabs and also had to close all swipeables when switching tabs. Using react-navigation as the navigation solution. Here is a gist in case someone needs it https://gist.github.com/barunprasad/a738d944fa9abf4e6993f719b13827ad

@Lsleiman this solves my issue! Thanks alot

Here what I did:


const EMPTY_KEY = ''; 

const row: Array<any> = [];
const [key, setKey] = React.useState<string | any>(EMPTY_KEY);

const handleWillOpen = (index : any) => () => (key !== EMPTY_KEY) && (key !== index) && row[key].close();

const handleOpen = (index : any) => () => setKey(index);

return <Swipeable 
              ref={ref => row[index] = ref}
              {....renders}
              onSwipeableRightWillOpen={handleWillOpen(index)}
              onSwipeableLeftWillOpen={handleWillOpen(index)}
              onSwipeableOpen={handleOpen(index)}>
              <View
                {....components}
            </View> 
          </Swipeable>

not using useCallback, or creating context whatever, just save the key, and make sure that before open some different key close the prior one.

Was this page helpful?
0 / 5 - 0 ratings