Skip to content

Commit dd35084

Browse files
authored
Fix DeckSwiper and Swiper with stateful children (#678)
* Re-render deck swiper cards on state change * Added monkey patch for swiper to allow state-changing components * Remove unneeded chain operator
1 parent 79c7071 commit dd35084

File tree

2 files changed

+53
-16
lines changed

2 files changed

+53
-16
lines changed

packages/core/src/components/DeckSwiper/DeckSwiper.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ const DeckSwiper = <T extends object>({
5353
);
5454
}
5555

56+
const deckSwiperRef = React.useRef<DeckSwiperComponent<T>>(null);
57+
5658
const childrenArray = React.useMemo(
5759
() => React.Children.toArray(children),
5860
[children]
@@ -89,6 +91,14 @@ const DeckSwiper = <T extends object>({
8991
}
9092
};
9193

94+
/*
95+
react-native-deck-swiper does not re-render cards when parent state changes
96+
This forces an update on every re-render to reflect any parent state changes
97+
*/
98+
React.useEffect(() => {
99+
deckSwiperRef.current?.forceUpdate();
100+
});
101+
92102
/**
93103
* By default react-native-deck-swiper positions everything with absolute position.
94104
* To overcome this, it is wrapped in a View to be able to add the component in any layout structure.
@@ -103,6 +113,7 @@ const DeckSwiper = <T extends object>({
103113
<View>
104114
<View style={styles.containerHeightFiller}>{renderFirstCard()}</View>
105115
<DeckSwiperComponent
116+
ref={deckSwiperRef}
106117
cards={cardsData as any[]}
107118
renderCard={renderCard}
108119
keyExtractor={cardKeyExtractor}

packages/core/src/components/Swiper/Swiper.tsx

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,16 @@ const Swiper = ({
4040
data,
4141
keyExtractor,
4242
renderItem,
43-
children,
43+
children: childrenProp,
4444
onIndexChanged: onIndexChangedProp,
4545
onSwipe,
4646
onSwipedNext,
4747
onSwipedPrevious,
4848
style,
4949
}: SwiperProps<any>) => {
5050
const [currentIndex, setCurrentIndex] = React.useState(0);
51-
const numberOfItems = data?.length ?? React.Children.count(children);
51+
const numberOfItems = data?.length ?? React.Children.count(childrenProp);
52+
const swiperRef = React.useRef<any>(null);
5253

5354
const onIndexChanged = (index: number) => {
5455
const previous = currentIndex;
@@ -71,10 +72,48 @@ const Swiper = ({
7172
onSwipe?.(previous);
7273
};
7374

75+
const children: React.ReactNode = React.useMemo(
76+
() =>
77+
data && renderItem
78+
? data.map((item, index) => {
79+
const component = renderItem({ item, index });
80+
81+
if (!component) {
82+
return null;
83+
}
84+
85+
const key = keyExtractor ? keyExtractor(item, index) : index;
86+
return React.cloneElement(component, {
87+
key,
88+
});
89+
})
90+
: childrenProp,
91+
[childrenProp, data, renderItem, keyExtractor]
92+
);
93+
94+
/*
95+
react-native-web-swiper assigns it's 'children' attribute as follows: `children = (() => React.Children.toArray(this.props.children))();`
96+
This is probelematic when state is involved due to anoynmous function effectivley creating new components everytime, losing any state
97+
98+
This is a monkey patch that updates the 'children' attribute to just use the children from the props
99+
Can be removed when/if https://github.com/reactrondev/react-native-web-swiper/pull/102 is merged
100+
*/
101+
React.useEffect(() => {
102+
const childrenArray = React.Children.toArray(
103+
swiperRef.current?.props?.children
104+
);
105+
if (swiperRef.current) {
106+
swiperRef.current.children = childrenArray;
107+
swiperRef.current.count = childrenArray.length;
108+
swiperRef.current.forceUpdate();
109+
}
110+
}, [children]);
111+
74112
return (
75113
<View style={style}>
76114
{/* @ts-ignore */}
77115
<SwiperComponent
116+
ref={swiperRef}
78117
from={from}
79118
loop={loop}
80119
timeout={timeout}
@@ -94,20 +133,7 @@ const Swiper = ({
94133
: {}),
95134
}}
96135
>
97-
{data && renderItem
98-
? data.map((item, index) => {
99-
const component = renderItem({ item, index });
100-
101-
if (!component) {
102-
return null;
103-
}
104-
105-
const key = keyExtractor ? keyExtractor(item, index) : index;
106-
return React.cloneElement(component, {
107-
key,
108-
});
109-
})
110-
: children}
136+
{children}
111137
</SwiperComponent>
112138
</View>
113139
);

0 commit comments

Comments
 (0)