Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cards not updating when state changes after all cards were swiped [Possible Solution] #94

Open
nofacez opened this issue Nov 29, 2022 · 2 comments

Comments

@nofacez
Copy link

nofacez commented Nov 29, 2022

❗️ First of all, if you are updating the cards list dynamically (e.g. adding new cards when there are 5 cards left in the current stack) and you are still facing the issue when the list doesn't update, please, make sure you have the latest version of the package. This issue should be fixed there.

Now, let's talk about our patient. Swiper doesn't update state after all cards were swiped because it considers the stack is finished and it stops adding new cards to it. Here the part of the code responsible for this:

  renderStack = () => {
    const { firstCardIndex, swipedAllCards } = this.state
    const { cards } = this.props
    const renderedCards = []
    let { stackSize, infinite, showSecondCard } = this.props
    let index = firstCardIndex
    let firstCard = true
    let cardPosition = 0

    while (stackSize-- > 0 && (firstCard || showSecondCard) && !swipedAllCards) {
      const key = this.getCardKey(cards[index], index)
      this.pushCardToStack(renderedCards, index, cardPosition, key, firstCard)

      firstCard = false

      if (index === cards.length - 1) {
        if (!infinite) break
        index = 0
      } else {
        index++
      }
      cardPosition++
    }
    return renderedCards
  }

swipedAllCards is a state variable, that is being set here

  incrementCardIndex = onSwiped => {
    const { firstCardIndex } = this.state
    const { infinite } = this.props
    let newCardIndex = firstCardIndex + 1
    let swipedAllCards = false

    this.onSwipedCallbacks(onSwiped)

    const allSwipedCheck = () => newCardIndex === this.props.cards.length

    if (allSwipedCheck()) {
      if (!infinite) {
        this.props.onSwipedAll()
        // onSwipeAll may have added cards
        if (allSwipedCheck()) {
          swipedAllCards = true // look here
        }
      } else {
        newCardIndex = 0;
      }
    }

    this.setCardIndex(newCardIndex, swipedAllCards)
  }

So once it is set to true it won't ever change to false because that stack just stops rendering.

How did I manage to fix it?

❗️DISCLAIMER: I don't think this is the best solution. This is why I've opened this issue, so we could find a more universal solution.

I've moved swipedAllCards property from state to props because I think we can decide ourselves when the list is finished or not. I've also added a isLoading prop to allow managing loading state of the swiper. To prevent Swiper from stopping rendering cards inside while case I've added CardListLoadingComponent and CardListEmptyComponent that are shown during the loading and empty state. Here is my renderStack function:

  renderStack = () => {
    const { firstCardIndex } = this.state;
    const { swipedAllCards } = this.props;
    const renderedCards = [];
    let {
      cards,
      stackSize,
      infinite,
      showSecondCard,
      loading,
      CardListLoadingComponent,
      CardListEmptyComponent,
    } = this.props;
    let index = firstCardIndex;
    let firstCard = true;
    let cardPosition = 0;

    if (swipedAllCards && loading) { // check for loading
      return CardListLoadingComponent;
    }

    if (swipedAllCards && !loading) { // check for empty
      return CardListEmptyComponent;
    }

    while (stackSize-- > 0 && (firstCard || showSecondCard)) {
      const key = this.getCardKey(cards[index], index);
      this.pushCardToStack(renderedCards, index, cardPosition, key, firstCard);

      firstCard = false;

      if (index === cards.length - 1) {
        if (!infinite) break;
        index = 0;
      } else {
        index++;
      }
      cardPosition++;
    }
    return renderedCards;
  };

And here is my incrementCardIndex function:

  incrementCardIndex = (onSwiped) => {
    const { firstCardIndex } = this.state;
    const { infinite, swipedAllCards } = this.props;
    let newCardIndex = firstCardIndex + 1;

    this.onSwipedCallbacks(onSwiped);

    const allSwipedCheck = () => newCardIndex === this.props.cards.length;

    if (allSwipedCheck()) {
      if (!infinite) {
        this.props.onSwipedAll();
      } else {
        newCardIndex = firstCardIndex;
      }
    }

    this.setCardIndex(newCardIndex, swipedAllCards);
  };

After this refactoring I had a working Deck Swiper so I stopped looking for a more reliable and universal solution. We have a heavy backend request for the new stack of cards, so while the request is pending the loading props is set to true and loading component is shown. When backend stops sending new cards the swipedAllCards is set to true and empty component is shown.

@mkhoussid
Copy link

Why not just set an integer as a key property on the Swiper component and just increment it whenever onSwiped fires?

@astevensLogi
Copy link

Why not just set an integer as a key property on the Swiper component and just increment it whenever onSwiped fires?

This was the much easier fix for me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants