Как повторно отображать карты в свайпе с помощью реакции-native-deck-swiper

У меня возникла проблема с обновлением содержимого карточек в свайпе с помощью react-native-deck-swiper. У меня есть кнопка, внешняя по отношению к свайпу, и при нажатии на нее я хочу обновить содержимое текущей карты и вызвать повторную визуализацию, чтобы отразить изменения.

Мне удалось правильно обновить состояние элемента, но карточка не отображается повторно сразу. Он отображается снова только тогда, когда я начинаю смахивать элемент.

В документации предлагается возможное исправление, в котором говорится: «Возможное исправление ситуации — установка cardIndex на родительском компоненте всякий раз, когда требуется повторный рендеринг колоды». Однако моя попытка реализовать это не увенчалась успехом.

Я заметил, что изменение отображается, когда требуется условие наложения (даже если наложение не установлено).

Ниже приведен воспроизводимый пример проблемы:

import React, { useRef, useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import Swiper from 'react-native-deck-swiper';

const cards = [
    { color: 'red', updated: false },
    { color: 'blue', updated: false },
    { color: 'green', updated: false },
    { color: 'yellow', updated: false },
    { color: 'purple', updated: false },
];

export default function App() {
    const [cardIndex, setCardIndex] = useState(0);
    const swiperRef = useRef(null);

    const updateCard = () => {
        cards[cardIndex].updated = !cards[cardIndex].updated;
        // Force re-render??
        setCardIndex(cardIndex); 
    };

    const onSwiped = () => {
        setCardIndex((cardIndex + 1) % cards.length); 
    };

    const renderCard = (card) => (
        <View style = {[styles.card, { backgroundColor: card.color }]}>
            {card.updated && <Text style = {styles.updatedText}>UPDATED</Text>}
        </View>
    );

    return (
        <View style = {styles.container}>
            <Swiper
                ref = {swiperRef}
                cards = {cards}
                renderCard = {renderCard}
                onSwiped = {onSwiped}
                onSwipedLeft = {onSwiped}
                onSwipedRight = {onSwiped}
                cardIndex = {cardIndex}
                infinite
            />
            <TouchableOpacity style = {styles.button} onPress = {updateCard}>
                <Text style = {styles.buttonText}>Update card</Text>
            </TouchableOpacity>
        </View>
    );
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#f7f7f7',
        alignItems: 'center',
        justifyContent: 'center',
    },
    card: {
        width: '80%',
        height: '80%',
        justifyContent: 'center',
        alignItems: 'center',
        alignSelf: 'center',
      },
    updatedText: {
        position: 'absolute',
        fontSize: 24,
        fontWeight: 'bold',
        color: 'black',
    },
    button: {
        position: 'absolute',
        bottom: 20,
        padding: 10,
        backgroundColor: 'blue',
        borderRadius: 5,
    },
    buttonText: {
        color: 'white',
        fontSize: 16,
    },
});

Я был бы признателен за любую информацию о том, как решить эту проблему и заставить карточку перерисовываться сразу после нажатия кнопки. Спасибо!

В их документах есть что-то об этом npmjs.com/package/…

Devon Ray 02.05.2024 06:04

@DevonRay Да, они что-то говорят об этом в документации (которую я цитирую в своем вопросе), но, насколько я понимаю, это не работает.

Alexandre 02.05.2024 06:14

вы устанавливали его обратно на cardIndex, его нужно изменить, чтобы он работал +1 или что-то в этом роде

Devon Ray 02.05.2024 22:24
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
3
125
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Сначала я подумал, что проблема в том, что вы не храните карты в переменной состояния, но после этого я обнаружил, что карты по-прежнему не обновляются, что заставляет меня полагать, что компонент Swiper кэширует карту при инициализации и игнорирует все ее обновления. . Здесь в игру вступает ключ . Компонент будет повторно инициализирован при изменении его ключа, поэтому, если вы сохраните swiperKey в состоянии и обновите его, карты будут повторно инициализированы ( демо):

import React, { useRef, useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import Swiper from 'react-native-deck-swiper';



const getRandomInt = (min=0,max=1)=>{
  const range = max - min
  return Math.floor(Math.random()*range)+min
}

const initialCardState = [
  { color: 'red', updated: false, },
  { color: 'blue', updated: false, },
  { color: 'green', updated: false, },
  { color: 'yellow', updated: false, },
  { color: 'purple', updated: false, },
];
// since this function doesnt depend on state move it outside of
// App; otherwise will be re-created on every render
const RenderCard = (card) => (
  <View style = {[styles.card, { backgroundColor: card.color }]}>
    {card.updated && <Text style = {styles.updatedText}>UPDATED</Text>}
  </View>
);
export default function App() {
  const [cardIndex, setCardIndex] = useState(0);
  // since cards values can change should be stored in state
  const [cards, setCards] = useState(initialCardState);
  const swiperRef = useRef(null);
  const [swiperKey, setSwiperKey] = useState("swiper-key")
  const updateCard = () => {
    setCards((prev) => {
      const newCards = [...prev];
      newCards[cardIndex].updated = !newCards[cardIndex].updated;
      return newCards;
    });
    // since setting the state isnt enough to get
    // the swiper component to re-render
    setSwiperKey(prev=>prev+getRandomInt(0,10))

  };

  const onSwiped = () => {
    // use the callback version of state setters
    // for better reliability
    setCardIndex((prev) => (prev + 1) % cards.length);
  };

  return (
    <View style = {styles.container}>
      <Swiper
        key = {swiperKey}
        ref = {swiperRef}
        cards = {cards}
        renderCard = {RenderCard}
        onSwipedLeft = {onSwiped}
        onSwipedRight = {onSwiped}
        cardIndex = {cardIndex}
        infinite
      />
      <TouchableOpacity style = {styles.button} onPress = {updateCard}>
        <Text style = {styles.buttonText}>Update card</Text>
      </TouchableOpacity>
    </View>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f7f7f7',
    alignItems: 'center',
    justifyContent: 'center',
  },
  card: {
    width: '80%',
    height: '80%',
    justifyContent: 'center',
    alignItems: 'center',
    alignSelf: 'center',
  },
  updatedText: {
    position: 'absolute',
    fontSize: 24,
    fontWeight: 'bold',
    color: 'black',
  },
  button: {
    position: 'absolute',
    bottom: 20,
    padding: 10,
    backgroundColor: 'blue',
    borderRadius: 5,
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
  },
});

Хорошо, это работает. Но я думаю, это дороже, чем просто перерисовать карту? Я попробую это сделать в своем более сложном случае использования, чтобы посмотреть, есть ли у него недостатки в производительности.

Alexandre 02.05.2024 06:17
import React, { useRef, useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import Swiper from 'react-native-deck-swiper';

const cards = [
    { color: 'red', updated: false },
    { color: 'blue', updated: false },
    { color: 'green', updated: false },
    { color: 'yellow', updated: false },
    { color: 'purple', updated: false },
];

export default function App() {
    const [cardIndex, setCardIndex] = useState(0);
    const [swiperKey, setSwiperKey] = useState(Date.now()); // Initialize swiperKey with current timestamp
    const swiperRef = useRef(null);

    const updateCard = () => {
        cards[cardIndex].updated = !cards[cardIndex].updated;
        setSwiperKey(Date.now()); // Change swiperKey to trigger re-render
    };

    const onSwiped = () => {
        setCardIndex((cardIndex + 1) % cards.length);
    };

    const renderCard = (card) => (
        <View style = {[styles.card, { backgroundColor: card.color }]}>
            {card.updated && <Text style = {styles.updatedText}>UPDATED</Text>}
        </View>
    );

    return (
        <View style = {styles.container}>
            <Swiper
                key = {swiperKey} // Change key to trigger re-render
                ref = {swiperRef}
                cards = {cards}
                renderCard = {renderCard}
                onSwiped = {onSwiped}
                onSwipedLeft = {onSwiped}
                onSwipedRight = {onSwiped}
                cardIndex = {cardIndex}
                infinite
            />
            <TouchableOpacity style = {styles.button} onPress = {updateCard}>
                <Text style = {styles.buttonText}>Update card</Text>
            </TouchableOpacity>
        </View>
    );
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#f7f7f7',
        alignItems: 'center',
        justifyContent: 'center',
    },
    card: {
        width: '80%',
        height: '80%',
        justifyContent: 'center',
        alignItems: 'center',
        alignSelf: 'center',
    },
    updatedText: {
        position: 'absolute',
        fontSize: 24,
        fontWeight: 'bold',
        color: 'black',
    },
    button: {
        position: 'absolute',
        bottom: 20,
        padding: 10,
        backgroundColor: 'blue',
        borderRadius: 5,
    },
    buttonText: {
        color: 'white',
        fontSize: 16,
    },
});

Попробуйте это; он обновит содержимое карты.

Другие вопросы по теме