React-native-swiper: Dynamically add children at the beginning

Created on 6 Sep 2016  路  16Comments  路  Source: leecade/react-native-swiper

I'm trying to implement a Calendar (such as Google Calendar) and I'm having trouble with the swipe left feature.

Since I don't want to load all the existing previous months since 1900, I simply want to add/load them dynamically. Let's say I start with three months and set my initial index to be in the middle. The swiper will look like this:
[ ] [ x ] [ ]

When I swipe to right, I need to add a month to my content, so I simply push a new month and the index moves by one, resulting in:
[ ] [ ] [ x ] [ ]

The problem is when I swipe left. The index of the swiper will naturally move by one to the left, but I also need to add one new month at the beginning. If I use the event onMomentumScrollEnd to handle my logic, the index has already changed when I'm adding a new month at the beginning, which result in a bug. The index does not seem to be synced with its children.

I'm a bit loss on how I could implement this.

Most helpful comment

@ALWAL12 not sure if you still need this but I sort of came up with the a solution that is working quite well for me.

I use a swiper component with 3 screens, set initial screen to index 1 (middle screen).

          <Swiper
            loadMinimalSize={0}
            index={1}
            loadMinimal
            onScrollBeginDrag={(e, { index }, context) => this.setState({ swiperOldIndex: index })}
            onMomentumScrollEnd={(e, { index }, context) => this.setState({ swiperCurrentIndex: index })}
          >

            {this._renderScreenComponent()}

            {this._renderScreenComponent()}

            {this._renderScreenComponent()}

          </Swiper>

How it works is that when the user swipes you update the ScreenComponent with the new data. Essential you only have one ScreenComponent, the other two are used as placeholders so that the user can swipe and also to determine the swipe direction which you can then use to load the data for the ScreenComponent. To determine the direction I use this:

_getSwipeDirection(oldIndex, newIndex) {
    if (oldIndex === 0 && newIndex === 2) {
      return -1;
    } else if (oldIndex === 2 && newIndex === 0) {
      return 1;
    } else if (newIndex > oldIndex) {
      return 1;
    } else if (newIndex < oldIndex) {
      return -1;
    }
    return 0;
  }

Everytime you swipe, a loader appears before the component loads (if you turn this off then the previous screen component will show during the scroll until the scroll is completed then the direction can be determined and the ScreenComponent can be updated). The loader could be a deal breaker for some but for me it works quite well since I need to fetch data anyway.

All 16 comments

Interested, I just posted another question #278 on how swiping too quick could cause issues. I didn't try it out yet, so I didn't realize I would get the problems you're describing. I'm curious if you work it out, and I'll also let you know if I figure something out.

Im having trouble solving the same problem for the last 48h, would like to see if someone already solved it

@ALWAL12 did you come up with a way to do this?

@superandrew213 Unfortunately no. We came up with a non-swipeable solution instead, but clearly non-optimal. Someday, I'll look forward to achieve it with a solution of my own.

@ALWAL12 not sure if you still need this but I sort of came up with the a solution that is working quite well for me.

I use a swiper component with 3 screens, set initial screen to index 1 (middle screen).

          <Swiper
            loadMinimalSize={0}
            index={1}
            loadMinimal
            onScrollBeginDrag={(e, { index }, context) => this.setState({ swiperOldIndex: index })}
            onMomentumScrollEnd={(e, { index }, context) => this.setState({ swiperCurrentIndex: index })}
          >

            {this._renderScreenComponent()}

            {this._renderScreenComponent()}

            {this._renderScreenComponent()}

          </Swiper>

How it works is that when the user swipes you update the ScreenComponent with the new data. Essential you only have one ScreenComponent, the other two are used as placeholders so that the user can swipe and also to determine the swipe direction which you can then use to load the data for the ScreenComponent. To determine the direction I use this:

_getSwipeDirection(oldIndex, newIndex) {
    if (oldIndex === 0 && newIndex === 2) {
      return -1;
    } else if (oldIndex === 2 && newIndex === 0) {
      return 1;
    } else if (newIndex > oldIndex) {
      return 1;
    } else if (newIndex < oldIndex) {
      return -1;
    }
    return 0;
  }

Everytime you swipe, a loader appears before the component loads (if you turn this off then the previous screen component will show during the scroll until the scroll is completed then the direction can be determined and the ScreenComponent can be updated). The loader could be a deal breaker for some but for me it works quite well since I need to fetch data anyway.

Thanks a lot for sharing, seems like a nice solution! But why not prefetch the other two screen components? In my case I have very little data, and will have it loaded already, so on each swipe, I will update screen 1 and 3 with the correct data, so there is no glitch when the data changes.

Yes you can prefetch the data and have it ready to update the screens but the glitch will still be there because we are only using one ScreenComponent duplicated 3 times. So each time you swipe you need to update this component with the new data. That's why we show the loader in between.

If you have little data why do you need to dynamically add more views to the swiper? You can just add all the views to it.

My usage is a week swiper, where each week has a few data entries, but I thought it might lag if I add 52 weeks to the swiper, if I wanted to make it possible to go one year into the future. And what would happen after that? So it seemed more resource saving to do it like this. Does that make sense?

Yes, I'm using it for the same purpose :). I have a lot of images in my calendar though. If you only have a bit of text then I think 52 weeks won't be a big performance hit.

Cool, if you have a beta or video or anything, I'd love to see it. I'm not developing at the moment, but will look into this again in the future. Good luck with your project!

This sounds like an interesting idea; Were there any updates to this?

@superandrew213 can you explain more how to implement this._renderScreenComponent() and how it uses _getSwipeDirection(oldIndex, newIndex) ?

this._renderScreenComponent() is just the view you want to show. This gets rendered 3 times and is the same component. You are only showing the middle one (index = 1 is fixed).

The reason we add the other two is so that we can get the swipe direction, which we track with this:

onScrollBeginDrag={(e, { index }, context) => this.setState({ swiperOldIndex: index })}
onMomentumScrollEnd={(e, { index }, context) => this.setState({ swiperCurrentIndex: index })}

Then you can use _getSwipeDirection(oldIndex, newIndex) to find the swipe direction and update your this._renderScreenComponent() with new data.

@ALWAL12 not sure if you still need this but I sort of came up with the a solution that is working quite well for me.

I use a swiper component with 3 screens, set initial screen to index 1 (middle screen).

          <Swiper
            loadMinimalSize={0}
            index={1}
            loadMinimal
            onScrollBeginDrag={(e, { index }, context) => this.setState({ swiperOldIndex: index })}
            onMomentumScrollEnd={(e, { index }, context) => this.setState({ swiperCurrentIndex: index })}
          >

            {this._renderScreenComponent()}

            {this._renderScreenComponent()}

            {this._renderScreenComponent()}

          </Swiper>

How it works is that when the user swipes you update the ScreenComponent with the new data. Essential you only have one ScreenComponent, the other two are used as placeholders so that the user can swipe and also to determine the swipe direction which you can then use to load the data for the ScreenComponent. To determine the direction I use this:

_getSwipeDirection(oldIndex, newIndex) {
    if (oldIndex === 0 && newIndex === 2) {
      return -1;
    } else if (oldIndex === 2 && newIndex === 0) {
      return 1;
    } else if (newIndex > oldIndex) {
      return 1;
    } else if (newIndex < oldIndex) {
      return -1;
    }
    return 0;
  }

Everytime you swipe, a loader appears before the component loads (if you turn this off then the previous screen component will show during the scroll until the scroll is completed then the direction can be determined and the ScreenComponent can be updated). The loader could be a deal breaker for some but for me it works quite well since I need to fetch data anyway.

Hi @superandrew213 ,
have your finally success using your method? which version of swiper are you using?
I am doing similar things but I am displaying endless list of articles in the same news section BUT after the user swipe to left or right and the system re-rendering the 3 views, although I set the index to 1, the focus view still becomes the first one (i.e. index=0). Would you give me some hint?

Regards
Richard

I replaced onScrollBeginDrag and onMomentumScrollEnd on _onIndexChanged_

this.state = {
  oldIndex: 1,
};

_getSwipeDirection = (newIndex) => {
    const {oldIndex} = this.state;

    if (oldIndex === 0 && newIndex === 2) {
      this.prevDate();
    } else if (oldIndex === 2 && newIndex === 0) {
      this.nextDate();
    } else if (newIndex > oldIndex) {
      this.nextDate();
    } else if (newIndex < oldIndex) {
      this.prevDate();
    }
};

render(){
  return(
    <Swiper
      showsPagination={false}
      index={1}
      loadMinimal
      loadMinimalSize={0}
      key={Date.now()}
      onIndexChanged={index => this._getSwipeDirection(index)}
    >
              {this.renderCells()}
              {this.renderCells()}
              {this.renderCells()}
    </Swiper>
  )
}
_getSwipeDirection(oldIndex, newIndex) {
    if (oldIndex === 0 && newIndex === 2) {
      return -1;
    } else if (oldIndex === 2 && newIndex === 0) {
      return 1;
    } else if (newIndex > oldIndex) {
      return 1;
    } else if (newIndex < oldIndex) {
      return -1;
    }
    return 0;
  }

When does the _"getSwipeDirection" get called in the component

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nicolabortignon picture nicolabortignon  路  3Comments

agzuniverse picture agzuniverse  路  3Comments

ghost picture ghost  路  3Comments

Liqiankun picture Liqiankun  路  3Comments

kliuj picture kliuj  路  3Comments