我在使用
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,
},
});
如果您能提供有关如何解决此问题并让卡片在按下按钮后立即重新渲染的见解,我将不胜感激。谢谢!
起初,我认为问题是你没有将卡片存储在状态变量中,但这样做之后,我发现卡片仍然不会更新,这让我相信 Swiper 组件在初始化时缓存卡片并忽略所有更新它。这就是 key 道具发挥作用的地方。当组件的 key 发生变化时,组件将被重新初始化,因此,如果您将 swiperKey 存储在状态中并更新它,卡片将被重新初始化(demo):
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,
},
});