У меня возникла проблема с обновлением содержимого карточек в свайпе с помощью 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,
},
});
Я был бы признателен за любую информацию о том, как решить эту проблему и заставить карточку перерисовываться сразу после нажатия кнопки. Спасибо!
@DevonRay Да, они что-то говорят об этом в документации (которую я цитирую в своем вопросе), но, насколько я понимаю, это не работает.
вы устанавливали его обратно на cardIndex, его нужно изменить, чтобы он работал +1 или что-то в этом роде
Сначала я подумал, что проблема в том, что вы не храните карты в переменной состояния, но после этого я обнаружил, что карты по-прежнему не обновляются, что заставляет меня полагать, что компонент 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,
},
});
Хорошо, это работает. Но я думаю, это дороже, чем просто перерисовать карту? Я попробую это сделать в своем более сложном случае использования, чтобы посмотреть, есть ли у него недостатки в производительности.
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,
},
});
Попробуйте это; он обновит содержимое карты.
В их документах есть что-то об этом npmjs.com/package/…