question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Issues showing new cards after paginated data is returned and added to the card props list

See original GitHub issue

Issue

When dynamically adding elements to the Swiper card prop, the newly added properties will not appear on the screen.

Longer Description

My issue comes from the following situation: My data is paginated, so I get the first page of data initially which consists of 5 elements which loads just fine and I am able to swipe on them no problem. I begin to load page two when the user is 2 away from the end of the five, once the data is returned I concat the array returned with page 2 data on to the array of page 1 data. This is where the issue is: The first five from page 1 show perfectly fine and I am able to interact with them flawlessly but when I try to concat page two to the existing array passed into the props of the Swiper the new items dont show up.

Expectation

I would expect when I modify the array being passed into the cards prop that change is reflected in the swiper and I can interact with the newly added array elements

Video

Ignore Ugly UI, its under construction…

https://user-images.githubusercontent.com/26589485/109442265-f091cc00-79f4-11eb-8eaf-132eb5552208.mp4

As you can see in the video the total property correctly updates to 10 after the second page of data is loaded in but once I reach the end of the first pages data the swiper fails to show the next 5 elements (the second page)

Related issues I have visited numerous times

#153, This Comment, #192, #189, and This Section in the Readme

Code

Swiper

  const [index, setIndex] = useState(0);
  const [products, setProducts] = useState<IProduct[]>([]);
...
const getProductsByTag = () => {
    axios.get('/products/list', {
      params: { tags: tags.join(','), page, size: 5 }
    }).then((res) => {
      const { products: internalProds, count } = res.data;
      setTotalSize(count);
      setProducts(products.concat(internalProds));
    }).catch((err) => {
      setError('Error getting products');
    })
  }
...
  const userLikeProduct = (product: IProduct) => {
    setIndex((index + 1) % products.length);
    transitionRef.current.animateNextTransition();

   if(products.length - 2 <= index) {
        console.log('getting more products in like product')
        setPage(page++)
        getProductsByTag();
      }
  }
...
  const renderCard = (tProduct: IProduct, tIndex: number) => {
    return <Card key={tProduct._id} index={tIndex} card={tProduct} />
  }
...
  useDidMountEffect(() => {
    getProductsByTag();
  }, [tags, page])
...
return (
....
{products.length ?
...
 <Swiper ref={swiperRef}
              cards={products}
              cardIndex={index}
              renderCard={(card, cardIndex) => {return renderCard(card, cardIndex)}}
              onSwipedRight={(num) => userLikeProduct(products[index])}
              onSwipedLeft={(num) => userDislikeProduct(products[index])}
              backgroundColor={'transparent'}
              onTapCard={() => userDislikeProduct(products[index])}
              cardVerticalMargin={0}
              cardHorizontalMargin={5}
              stackSize={stackSize}
              stackScale={3}
              stackSeparation={2}
              animateOverlayLabelsOpacity
              animateCardOpacity
              showSecondCard={true}
              disableTopSwipe
              disableBottomSwipe
              overlayLabels={{
                left: {
                  title: 'NOPE',
                  style: {
                    label: {
                      backgroundColor: colors.red,
                      borderColor: colors.red,
                      color: colors.white,
                      borderWidth: 1,
                      fontSize: 24
                    },
                    wrapper: {
                      flexDirection: 'column',
                      alignItems: 'flex-end',
                      justifyContent: 'flex-start',
                      marginTop: 20,
                      marginLeft: -20
                    }
                  }
                },
                right: {
                  title: 'LIKE',
                  style: {
                    label: {
                      backgroundColor: colors.blue,
                      borderColor: colors.blue,
                      color: colors.white,
                      borderWidth: 1,
                      fontSize: 24
                    },
                    wrapper: {
                      flexDirection: 'column',
                      alignItems: 'flex-start',
                      justifyContent: 'flex-start',
                      marginTop: 20,
                      marginLeft: 20
                    }
                  }
                }
              }}
            />
...
 :
        <View>
          <Text>Loading</Text>
        </View>
)

Package.json

{
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject",
    "test": "jest --watchAll"
  },
  "jest": {
    "preset": "jest-expo"
  },
  "dependencies": {
    "@cantum2/simply-lib": "^1.0.7",
    "@expo/react-native-action-sheet": "^3.8.0",
    "@expo/vector-icons": "^12.0.0",
    "@react-native-community/async-storage": "^1.12.1",
    "@react-native-community/masked-view": "0.1.10",
    "@react-navigation/bottom-tabs": "5.11.2",
    "@react-navigation/material-top-tabs": "^5.3.13",
    "@react-navigation/native": "~5.8.10",
    "@react-navigation/stack": "~5.12.8",
    "axios": "^0.21.1",
    "expo": "~40.0.0",
    "expo-asset": "~8.2.1",
    "expo-constants": "~9.3.0",
    "expo-font": "~8.4.0",
    "expo-linking": "~2.0.0",
    "expo-splash-screen": "~0.8.0",
    "expo-status-bar": "~1.0.3",
    "expo-web-browser": "~8.6.0",
    "react": "16.13.1",
    "react-dom": "16.13.1",
    "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
    "react-native-deck-swiper": "^2.0.5",
    "react-native-gesture-handler": "~1.8.0",
    "react-native-reanimated": "~1.13.0",
    "react-native-safe-area-context": "3.1.9",
    "react-native-screens": "~2.15.2",
    "react-native-tab-view": "^2.15.2",
    "react-native-web": "~0.13.12"
  },
  "devDependencies": {
    "@babel/core": "~7.9.0",
    "@types/react": "~16.9.35",
    "@types/react-native": "~0.63.2",
    "jest-expo": "~40.0.0",
    "typescript": "~4.0.0"
  },
  "private": true
}

If there is anything else you would like to see from me please feel free to let me know. Thank you.

Issue Analytics

  • State:open
  • Created 3 years ago
  • Comments:10

github_iconTop GitHub Comments

35reactions
Cantum2commented, Mar 17, 2021

The solution mentioned by @felire worked! Simply adding the length of the array as the key re-rendered my list with my paginated data. Here is what my code looks like that contains this:

...
export const SimplySwiper: FC<props> = ({ index, onUserDislike, onUserLike, products }) => {
    return (
        <Swiper ref={swiperRef}
            cards={products}
            cardIndex={index}
            renderCard={(card, cardIndex) => renderCard(card, cardIndex)}
            onSwipedRight={(num) => onUserLike(products[index])}
            onSwipedLeft={(num) => onUserDislike(products[index])}
            backgroundColor={'transparent'}
            onTapCard={() => onUserDislike(products[index])}
            cardVerticalMargin={0}
            cardHorizontalMargin={5}
            stackSize={stackSize}

            key={products.length} // HERE IS THE SOLUTION

            stackScale={3}
            stackSeparation={2}
            animateOverlayLabelsOpacity
            animateCardOpacity
            showSecondCard={true}
            disableTopSwipe
            disableBottomSwipe
...

I do believe this solution is a bit of a hack as we have to rerender the entire list every single swipe. I would like to leave this issue open as this issue has happened for dozens of developers and it would be great if the maintainers implemented a more elegant solution for this.

11reactions
felirecommented, Mar 15, 2021

Okey, I could make it work but with a lot of hacks: It is something like this:

This is how I implement the CustomSwiper, which implement the swiper:

              <CustomSwiper
                pets={[...candidates, { image: noMatchPets }]}
                onSwipedRight={onSwipe('like')}
                onSwipedLeft={onSwipe('dislike')}
                initialIndex={swipeIndex.current + 1}
                key={candidates.length}
                onTapCard={onTapCard}
                ref={swiper}
                isLastDataSet={isLastDataSet}
                offset={OFFSET}
                type={'match'}
              />

I am updating the key whenever the length of the candidates change so it will re-render, and since that is rerendering I can send it a reference and it will take the value of that reference to set the initial-value, since the list of candidates already has the old values but I am like ignoring them on the re-render because they were already swiped.

I mean, I had to make re-renders constantly whenever the data is modified.

I am re-rendering when the user unfocus the screen too.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Rendering large lists in React: 5 methods with examples
Explore five methods for easily rendering large lists in React, including pagination, infinite scroll, and several libraries.
Read more >
Pagination and Card Components with Ant Design (antd)?
Their code is below; all you'd have to do is pass in your data to the dataSource prop and have renderItem return a...
Read more >
How To Build Custom Pagination with React | DigitalOcean
Start a new React application using the create-react-app command. ... the country prop object must contain the following data:.
Read more >
Displaying a List in React Native: Map Method or FlatList ...
A data prop takes an array of data that needs to be rendered, and renderItem defines a function that takes data via parameters...
Read more >
React-admin - The List View
The <List> component fetches the list of records from the data provider, and renders the default list layout (title, buttons, filters, pagination). It...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found