-
Notifications
You must be signed in to change notification settings - Fork 47
Description
Hit an issue today after upgrading react-native-reanimated from 2.15.1 -> 3.5.4
Redbox error while developing on iOS & Android showed an issue in this packages index.js file for cannot read property 'prototype' of undefined react native ticker The issue pointed to the widthAnim value/function line.
I dug into this a bit and made some changes to the files to utilize better logic for running the animations and ultimately fixed the issue I was experiencing. I also solved a problem with the string length changing and preventing the last character(s) from animating.
Ex. Initial value rendered is $0.00 but changes after API fetches data to $52.50. The component would only animate the first three characters in the new string, leaving the 0 stale.
Ex. Initial value rendered is $0.00 but changes after API fetches data to $134.42. The component would only animate the first three characters in the new string, leaving the final 42 characters stale with no animation.
My changes fixes the Redbox bug as well as improves on the current animations running when the string value changes.
I created a patch file that includes all of this if anybody else experiences the issue.
diff --git a/node_modules/react-native-ticker/index.js b/node_modules/react-native-ticker/index.js
index a865f7b..817b863 100644
--- a/node_modules/react-native-ticker/index.js
+++ b/node_modules/react-native-ticker/index.js
@@ -11,7 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
};
import React, { useRef, useEffect, useState, Children } from "react";
import { StyleSheet, Text, View, I18nManager, } from "react-native";
-import Animated, { EasingNode } from "react-native-reanimated";
+import Animated, { EasingNode, useAnimatedStyle, useDerivedValue, useSharedValue, withTiming } from "react-native-reanimated";
const styles = StyleSheet.create({
row: {
flexDirection: I18nManager.isRTL ? "row-reverse" : "row",
@@ -35,62 +35,44 @@ const numberRange = range(10).map((p) => p + "");
const numAdditional = [",", "."];
const numberItems = [...numberRange, ...numAdditional];
const isNumber = (v) => !isNaN(parseInt(v));
-const getPosition = ({ text, items, height, }) => {
- const index = items.findIndex((p) => p === text);
- return index * height * -1;
-};
+
export const Tick = (_a) => {
var props = __rest(_a, []);
//@ts-ignore
return <TickItem {...props}/>;
};
-const useInitRef = (cb) => {
- const ref = useRef();
- if (!ref.current) {
- ref.current = cb();
- }
- return ref.current;
-};
+
const TickItem = ({ children, duration, textStyle, textProps, measureMap, rotateItems, }) => {
const measurement = measureMap[children];
- const position = getPosition({
- text: children,
- height: measurement.height,
- items: rotateItems,
- });
- const widthAnim = useInitRef(() => new Animated.Value(measurement.width));
- const stylePos = useInitRef(() => new Animated.Value(position));
+ const position = useSharedValue(0);
+
useEffect(() => {
- if (stylePos) {
- Animated.timing(stylePos, {
- toValue: position,
- duration,
- easing: EasingNode.linear,
- }).start();
- Animated.timing(widthAnim, {
- toValue: measurement.width,
- duration: 25,
- easing: EasingNode.linear,
- }).start();
+ position.value = rotateItems.findIndex((p) => p === children) * measurement.height * -1;
+ }, [children])
+
+ const randomizer = Math.floor(Math.random() * 4)
+ const widthAnim = useAnimatedStyle(() => {
+ return {
+ height: withTiming(measurement.height, {duration: 50}),
+ width: withTiming(measurement.width, {duration: 50})
}
- }, [position, measurement]);
- return (<Animated.View style={[
- {
- height: measurement.height,
- width: widthAnim,
- overflow: "hidden",
- },
- ]}>
- <Animated.View style={[
- {
- transform: [{ translateY: stylePos }],
- },
- ]}>
- {rotateItems.map((v) => (<Text key={v} {...textProps} style={[textStyle, { height: measurement.height }]}>
- {v}
- </Text>))}
- </Animated.View>
- </Animated.View>);
+ })
+ const stylePos = useAnimatedStyle(() => {
+ return {
+ transform: [{ translateY: withTiming(position.value, {duration: 1000 + randomizer * 200})}],
+ }
+ })
+
+
+ return (
+ <Animated.View style={[{overflow: "hidden"}, widthAnim]}>
+ <Animated.View style={stylePos}>
+ {rotateItems.map((v) => (<Text key={v} {...textProps} style={[textStyle, { height: measurement.height }]}>
+ {v}
+ </Text>))}
+ </Animated.View>
+ </Animated.View>
+ );
};
const Ticker = ({ duration = 250, containerStyle, textStyle, textProps, children }) => {
const [measured, setMeasured] = useState(false);
diff --git a/node_modules/react-native-ticker/index.tsx b/node_modules/react-native-ticker/index.tsx
index 1eb0eda..7ff8a52 100644
--- a/node_modules/react-native-ticker/index.tsx
+++ b/node_modules/react-native-ticker/index.tsx
@@ -1,4 +1,4 @@
-import React, { useRef, useEffect, useState, Children } from "react";
+import React, {useRef, useEffect, useState, Children} from 'react';
import {
StyleSheet,
Text,
@@ -7,16 +7,20 @@ import {
TextStyle,
TextProps,
I18nManager,
-} from "react-native";
-import Animated, { EasingNode } from "react-native-reanimated";
+} from 'react-native';
+import Animated, {
+ useAnimatedStyle,
+ useSharedValue,
+ withTiming,
+} from 'react-native-reanimated';
const styles = StyleSheet.create({
row: {
- flexDirection: I18nManager.isRTL ? "row-reverse" : "row",
- overflow: "hidden",
+ flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
+ overflow: 'hidden',
},
hide: {
- position: "absolute",
+ position: 'absolute',
top: 0,
left: 0,
opacity: 0,
@@ -29,10 +33,10 @@ const uniq = (values: string[]) => {
});
};
-const range = (length: number) => Array.from({ length }, (x, i) => i);
-const splitText = (text = "") => (text + "").split("");
-const numberRange = range(10).map((p) => p + "");
-const numAdditional = [",", "."];
+const range = (length: number) => Array.from({length}, (x, i) => i);
+const splitText = (text = '') => (text + '').split('');
+const numberRange = range(10).map((p) => p + '');
+const numAdditional = [',', '.'];
const numberItems = [...numberRange, ...numAdditional];
const isNumber = (v: string) => !isNaN(parseInt(v));
@@ -67,22 +71,13 @@ interface TickProps {
measureMap: MeasureMap;
}
-type MeasureMap = Record<string, { width: number; height: number }>;
+type MeasureMap = Record<string, {width: number; height: number}>;
-export const Tick = ({ ...props }: Partial<TickProps>) => {
+export const Tick = ({...props}: Partial<TickProps>) => {
//@ts-ignore
return <TickItem {...props} />;
};
-const useInitRef = (cb: () => Animated.Value<number>) => {
- const ref = useRef<Animated.Value<number>>();
- if (!ref.current) {
- ref.current = cb();
- }
-
- return ref.current;
-};
-
const TickItem = ({
children,
duration,
@@ -92,54 +87,40 @@ const TickItem = ({
rotateItems,
}: TickProps) => {
const measurement = measureMap[children];
-
- const position = getPosition({
- text: children,
- height: measurement.height,
- items: rotateItems,
- });
-
- const widthAnim = useInitRef(() => new Animated.Value(measurement.width));
- const stylePos = useInitRef(() => new Animated.Value(position));
+ const position = useSharedValue(0);
useEffect(() => {
- if (stylePos) {
- Animated.timing(stylePos, {
- toValue: position,
- duration,
- easing: EasingNode.linear,
- }).start();
- Animated.timing(widthAnim, {
- toValue: measurement.width,
- duration: 25,
- easing: EasingNode.linear,
- }).start();
- }
- }, [position, measurement]);
-
- return (
- <Animated.View
- style={[
+ position.value =
+ rotateItems.findIndex((p) => p === children) * measurement.height * -1;
+ }, [children]);
+
+ const randomizer = Math.floor(Math.random() * 4);
+ const widthAnim = useAnimatedStyle(() => {
+ return {
+ height: withTiming(measurement.height, {duration: 50}),
+ width: withTiming(measurement.width, {duration: 50}),
+ };
+ });
+ const stylePos = useAnimatedStyle(() => {
+ return {
+ transform: [
{
- height: measurement.height,
- width: widthAnim,
- overflow: "hidden",
+ translateY: withTiming(position.value, {
+ duration: 1000 + randomizer * duration,
+ }),
},
- ]}
- >
- <Animated.View
- style={[
- {
- transform: [{ translateY: stylePos }],
- },
- ]}
- >
+ ],
+ };
+ });
+
+ return (
+ <Animated.View style={[{overflow: 'hidden'}, widthAnim]}>
+ <Animated.View style={stylePos}>
{rotateItems.map((v) => (
<Text
key={v}
{...textProps}
- style={[textStyle, { height: measurement.height }]}
- >
+ style={[textStyle, {height: measurement.height}]}>
{v}
</Text>
))}
@@ -148,18 +129,25 @@ const TickItem = ({
);
};
-const Ticker = ({ duration = 250, containerStyle, textStyle, textProps, children }: Props) => {
+const Ticker = ({
+ duration = 250,
+ containerStyle,
+ textStyle,
+ textProps,
+ children,
+}: Props) => {
const [measured, setMeasured] = useState<boolean>(false);
const measureMap = useRef<MeasureMap>({});
const measureStrings: string[] = Children.map(children as any, (child) => {
- if (typeof child === "string" || typeof child === "number") {
+ if (typeof child === 'string' || typeof child === 'number') {
return splitText(`${child}`);
} else if (child) {
return child?.props && child?.props?.rotateItems;
}
}).reduce((acc, val) => acc.concat(val), []);
+ console.log('MEASURE: ', measureStrings);
const hasNumbers = measureStrings.find((v) => isNumber(v)) !== undefined;
const rotateItems = uniq([
...(hasNumbers ? numberItems : []),
@@ -183,7 +171,7 @@ const Ticker = ({ duration = 250, containerStyle, textStyle, textProps, children
<View style={[styles.row, containerStyle]}>
{measured === true &&
Children.map(children, (child) => {
- if (typeof child === "string" || typeof child === "number") {
+ if (typeof child === 'string' || typeof child === 'number') {
return splitText(`${child}`).map((text, index) => {
let items = isNumber(text) ? numberItems : [text];
return (
@@ -193,13 +181,13 @@ const Ticker = ({ duration = 250, containerStyle, textStyle, textProps, children
textStyle={textStyle}
textProps={textProps}
rotateItems={items}
- measureMap={measureMap.current}
- >
+ measureMap={measureMap.current}>
{text}
</TickItem>
);
});
} else {
+ console.log('RETURNED CLONED ELEMENT');
//@ts-ignore
return React.cloneElement(child, {
duration,
@@ -210,13 +198,13 @@ const Ticker = ({ duration = 250, containerStyle, textStyle, textProps, children
}
})}
{rotateItems.map((v) => {
+ console.log('ROTATE ITEMS');
return (
<Text
key={v}
{...textProps}
style={[textStyle, styles.hide]}
- onLayout={(e) => handleMeasure(e, v)}
- >
+ onLayout={(e) => handleMeasure(e, v)}>
{v}
</Text>
);
@@ -225,8 +213,8 @@ const Ticker = ({ duration = 250, containerStyle, textStyle, textProps, children
);
};
-Ticker.defaultProps = {
- duration: 250,
-};
+// Ticker.defaultProps = {
+// duration: 250,
+// };
export default Ticker;