react-native-animated-dots-carousel icon indicating copy to clipboard operation
react-native-animated-dots-carousel copied to clipboard

`Cannot read property `borderColor` of undefined

Open nateguy opened this issue 8 months ago • 3 comments

Getting an issue on sentry with the latest version where there are instances where an undefined type variable in react-native-animated-dots-carousel/src/CarouselDots/Dot/index.tsx ends up crashing my app. This was never an issue previously. Am wondering if it's due to any possibility that currentIndex ends up being out of range?

Image

nateguy avatar Mar 28 '25 03:03 nateguy

I believe this issue occurs because currentIndex is out of range. You can try validating your currentIndex value or applying my patch below.

I used patch-package to patch [email protected] for the project I'm working on.

Here is the diff that solved my problem:

diff --git a/node_modules/react-native-animated-dots-carousel/src/CarouselDots/index.tsx b/node_modules/react-native-animated-dots-carousel/src/CarouselDots/index.tsx
index e91f902..fb3100e 100644
--- a/node_modules/react-native-animated-dots-carousel/src/CarouselDots/index.tsx
+++ b/node_modules/react-native-animated-dots-carousel/src/CarouselDots/index.tsx
@@ -68,6 +68,7 @@ const calculateOffsetSize = (
   );
   return result.totalSize + result.offset * minimumSize;
 };
+
 const CarouselDots = ({
   length,
   currentIndex,
@@ -79,11 +80,14 @@ const CarouselDots = ({
   interpolateOpacityAndColor = true,
   duration = 500,
 }: CarouselDotsProps): JSX.Element => {
+  // Clamp currentIndex to ensure it's within [0, length - 1]
+  const clampedIndex = Math.max(0, Math.min(Math.floor(currentIndex), length - 1));
+
   const refScrollView = useRef<ScrollView>(null);
   const positiveMomentum = useRef<boolean>(false);
-  const prevIndex = usePrevious(currentIndex, currentIndex);
+  const prevIndex = usePrevious(clampedIndex, clampedIndex);
   const [carouselState, setCarouselState] = useState<CarouselState>({
-    currentIndex,
+    currentIndex: clampedIndex,
     state: 1,
   });
   const list = [...Array(length).keys()];
@@ -114,10 +118,11 @@ const CarouselDots = ({
     },
     [maxIndicators, offsetSizeMap]
   );
+
   useEffect(() => {
-    positiveMomentum.current = currentIndex - prevIndex > 0;
+    positiveMomentum.current = clampedIndex - prevIndex > 0;
     let internalState = carouselState.state;
-    internalState += currentIndex - prevIndex;
+    internalState += clampedIndex - prevIndex;
     const finalState = internalState;
     if (internalState > maxIndicators) {
       internalState = maxIndicators;
@@ -127,7 +132,7 @@ const CarouselDots = ({
     }
     if (internalState) {
       setCarouselState({
-        currentIndex,
+        currentIndex: clampedIndex,
         state: internalState,
       });
     }
@@ -136,10 +141,11 @@ const CarouselDots = ({
       length > maxIndicators &&
       (finalState > maxIndicators || finalState < 1)
     ) {
-      scrollTo(currentIndex);
+      scrollTo(clampedIndex);
     }
     // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [currentIndex, length, maxIndicators, scrollTo]);
+  }, [clampedIndex, length, maxIndicators, scrollTo]);
+
   const containerSize = useMemo(() => {
     return (
       decreasingDots.reduce(
@@ -173,7 +179,7 @@ const CarouselDots = ({
               inactiveIndicatorConfig={inactiveIndicatorConfig}
               verticalOrientation={verticalOrientation}
               decreasingDots={decreasingDots}
-              carouselState={carouselState}
+              carouselState={{ ...carouselState, currentIndex: clampedIndex }}
               interpolateOpacityAndColor={interpolateOpacityAndColor}
               duration={duration}
             />
@@ -182,11 +188,13 @@ const CarouselDots = ({
       </View>
     );
   }
+
   const invisibleFillerSize =
     decreasingDots.reduce(
       (acc, current) => calculateDecreasingDotSize(current) + acc,
       0
     ) / 2;
+
   return (
     <View
       style={[
@@ -222,7 +230,7 @@ const CarouselDots = ({
               inactiveIndicatorConfig={inactiveIndicatorConfig}
               decreasingDots={decreasingDots}
               verticalOrientation={verticalOrientation}
-              carouselState={carouselState}
+              carouselState={{ ...carouselState, currentIndex: clampedIndex }}
               interpolateOpacityAndColor={interpolateOpacityAndColor}
               duration={duration}
             />
@@ -273,6 +281,7 @@ const CarouselDotsWrapper = ({
       runOnJS(handleGoUp)(momentum);
     }
   };
+
   const gesture = Gesture.Pan()
     .onStart(() => {
       accDesplacementPos.value = 0;
@@ -341,4 +350,5 @@ const CarouselDotsWrapper = ({
     </View>
   );
 };
+
 export default CarouselDotsWrapper;

This issue body was partially generated by patch-package.

thanhcuong1990 avatar Apr 20 '25 16:04 thanhcuong1990

@thanhcuong1990 Could you create a pr with your fix?

frodriguez-hu avatar May 26 '25 17:05 frodriguez-hu

Let me know @thanhcuong1990 once you create the pr and I will take a look. I answered you from my work account

felire avatar Jun 02 '25 18:06 felire