How to pass dynamic ref in react-native-deck-swiper in react native?

1.1k Views Asked by At

I'm not used to using ref, so i have a question. I'm using react-native-deck-swiper to display some cards. People are able to vote by swiping the cards. To be as clear as possible :

  • My swiper is displayed as an item with other items from multiple types in my flatlist
  • My items are rendered conditionally by types of items, if my item is a swiper type, im displaying a swiper, if it is a simple text, i'm displaying a text etc...
  • So I potentially have multiple swipers in my flatlist

Here is the json to display my swipers :

{
            "id": 577,
            "type": "copernic",
            "entity": {
                "id": 715,
                "str_id": "rCckubRTIDz2D3N5wcKW",
                "display_name": "Force Républicaine",
                "name": "Force Républicaine",
                "title": "Dépense publique",
                "description": null,
                "type": 2,
                "str_type": "Pour / Contre / Neutre",
                "is_opened": 1,
                "str_is_opened": "Les utilisteurs peuvent soumettre des propositions",
                "order_propositions_by": 3,
                "display_votes": 1,
                "login_to_vote": 0,
                "str_login_to_vote": "Les utilisateurs ne doivent pas se connecter pour voter",
                "login_to_send_proposition": 1,
                "str_login_to_send_proposition": "Les utilisateurs doivent se connecter pour envoyer une proposition",
                "begins_at": "2021-03-02T17:45:55.000000Z",
                "ends_at": null,
                "propositions": [
                    {
                        "id": 6572,
                        "title": "Pensez-vous qu'il faille annuler la dette COVID ?",
                        "description": "",
                        "auteur": "Force Républicaine",
                        "type": 1,
                        "media_link": null,
                        "video_frame": null
                    },
                    {
                        "id": 6571,
                        "title": "Trouvez-vous la qualité des services publics en France à la hauteur du niveau de la dépense publique du pays ?",
                        "description": "",
                        "auteur": "Force Républicaine",
                        "type": 1,
                        "media_link": null,
                        "video_frame": null
                    }
                ]
            },
            "is_pinned": 0,
            "created_at": "2021-03-18T13:27:03.000000Z"
        },

So each swipers could have as a ref, the item.entity.id or the item.entity.id.

My swipers are displaying the item.entity.propositions as cards. Each card have thumbs to vote right, up or left. And I need a ref to do something as swiperRef.current.swipeRight() when we tap on a thumb or to tell the user that every cards from this swiper have been swiped by displaying a message. And now the message is displaying on all swipers and that't not what I want :)

Here's the code for my swiper :

if (
      item.type === "copernic" &&
      item.entity.str_type === "Pour / Contre / Neutre"
    ) {
       return (
         <View style={styles.swipperWrapper} key={item.id}>
          {isSwipedAll ? (
            <View style={styles.emptyCopernic}>
              <View style={styles.emptyCopernicColor} />
              <View style={styles.copContainer}>
                <Text style={styles.copTitle}>
                  You have swiped all the cards! Thanx for your participation !
                </Text>
              </View>
            </View>
          ) : (
            <Swiper
              ref={useSwiper}
              //animateCardOpacity
              containerStyle={styles.swiperContainer}
              cards={item.entity.propositions}
              renderCard={(card, index) => (
                <CardCopernic
                  {...index}
                  copKey={card.id}
                  copTitle={card.title}
                  copDesc={card.description}
                  strid={strid}
                  onPressNo={() => swipeLeft(card.id, index)}
                  onPressMaybe={() => swipeTop(card.id, index)}
                  onPressYes={() => swipeRight(card.id, index)}
                />
              )}
              onSwipedLeft={(card) => handleNo(card)}
              onSwipedTop={(card) => handleMaybe(card)}
              onSwipedRight={(card) => handleYes(card)}
              onSwipedAll={onSwipedAllCards}
              cardIndex={cardIndex}
              stackSize={3}
              stackScale={5}
              showSecondCard
              backgroundColor="white"
              cardVerticalMargin={2}
              disableBottomSwipe
              overlayLabels={{
                left: {
                  title: "Contre",
                  style: {
                    label: {
                      borderColor: "#FF8585",
                      color: "#FF8585",
                      borderWidth: 2,
                    },
                    wrapper: {
                      flexDirection: "column",
                      alignItems: "flex-end",
                      justifyContent: "flex-start",
                      marginTop: 30,
                      marginLeft: -30,
                    },
                  },
                },
                right: {
                  title: "Pour",
                  style: {
                    label: {
                      borderColor: "#FF8585",
                      color: "#FF8585",
                      borderWidth: 2,
                    },
                    wrapper: {
                      flexDirection: "column",
                      alignItems: "flex-start",
                      justifyContent: "flex-start",
                      marginTop: 30,
                      marginLeft: 30,
                    },
                  },
                },
                top: {
                  title: "Neutre",
                  style: {
                    label: {
                      borderColor: "#FF8585",
                      color: "#FF8585",
                      borderWidth: 2,
                    },
                    wrapper: {
                      flexDirection: "column",
                      alignItems: "center",
                      justifyContent: "center",
                    },
                  },
                },
              }}
            />
          )}
        </View>
      );

Here's one of the function to swipe the card to the left, right or top :

swipeLeft = async (cardId, index) => {
        console.log("CARDINDEX : ", index);

        const cardKey = cardId;
        const strid = item.entity.str_id;
        const token = await AsyncStorage.getItem("token");
        useSwiper.current[strid].swipeLeft(cardId);

        const requestOptions = {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "X-Requested-With": "XMLHttpRequest",
            Authorization: `Bearer ${token}`,
          },
          body: JSON.stringify({
            vote: "contre",
            proposition_id: `${cardKey}`,
            consultation_str_id: `${strid}`,
          }),
        };

        try {
          let response = await fetch(
            `${c.API_URL_DEV}/copernic/vote/add`,
            requestOptions
          );
          let json = await response.json();
          if (index === item.entity.propositions.length - 1) {
            setSwipedAll(true);
            await AsyncStorage.setItem("hasAllSwiped", JSON.stringify(true));
          }
          return json;
        } catch (error) {
          console.error(error);
        }
      };

Any help is appreciated ! I'm a little bit stuck here and not too familiar with refs haha x And my items are functional components :)

If you need more informations to help, let me know ! :)

1

There are 1 best solutions below

1
azundo On

Try encapsulating the related swiper code into a component. This will remove the need for dynamic refs and properly encapsulate state.

Something like:

function MySwiper({item}) {
  const swiperRef = useRef(null);
  const [isSwipedAll, setSwipedAll] = useState(false);
  const swipeLeft = useCallback(async (cardId, index) => {
    console.log("CARDINDEX : ", index);

    const cardKey = cardId;
    const strid = item.entity.str_id;
    const token = await AsyncStorage.getItem("token");
    swiperRef.current.swipeLeft(cardId);

    const requestOptions = {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Requested-With": "XMLHttpRequest",
        Authorization: `Bearer ${token}`,
      },
      body: JSON.stringify({
        vote: "contre",
        proposition_id: `${cardKey}`,
        consultation_str_id: `${strid}`,
      }),
    };

    try {
      let response = await fetch(
        `${c.API_URL_DEV}/copernic/vote/add`,
        requestOptions
      );
      let json = await response.json();
      if (index === item.entity.propositions.length - 1) {
        setSwipedAll(true);
        await AsyncStorage.setItem("hasAllSwiped", JSON.stringify(true)); // TODO: index this somehow by the item
      }
      return json;
    } catch (error) {
      console.error(error);
    }
  }, [item]);

  return (
    <View style={styles.swipperWrapper} key={item.id}>
      {isSwipedAll ? (
        <View style={styles.emptyCopernic}>
          <View style={styles.emptyCopernicColor} />
          <View style={styles.copContainer}>
            <Text style={styles.copTitle}>
              You have swiped all the cards! Thanx for your participation !
            </Text>
          </View>
        </View>
      ) : (
        <Swiper
          ref={useSwiper}
          //animateCardOpacity
          containerStyle={styles.swiperContainer}
          cards={item.entity.propositions}
          renderCard={(card, index) => (
            <CardCopernic
              {...index}
              copKey={card.id}
              copTitle={card.title}
              copDesc={card.description}
              strid={strid}
              onPressNo={swipeLeft}
              onPressMaybe={() => swipeTop(card.id, index)} // UPDATE
              onPressYes={() => swipeRight(card.id, index)} // UPDATE
            />
          )}
          onSwipedLeft={(card) => handleNo(card)} // UPDATE
          onSwipedTop={(card) => handleMaybe(card)} // UPDATE
          onSwipedRight={(card) => handleYes(card)} // UPDATE
          onSwipedAll={onSwipedAllCards}
          cardIndex={cardIndex}
          stackSize={3}
          stackScale={5}
          showSecondCard
          backgroundColor="white"
          cardVerticalMargin={2}
          disableBottomSwipe
          overlayLabels={...}
        />
      )}
    </View>
  );
}

You'll need to create the other callbacks and fix they storing of state in AsyncStorage to be keyed based on each item.