react-native-reanimated: [3.0.0-rc.10] Cannot read property 'prototype' of undefined when creating a `new Clock` instance
Description
When I try to new Clock()
it throws Cannot read property 'prototype' of undefined
.
Here’s the hook:
useCollapsible
import React, { useCallback } from 'react';
import type { LayoutChangeEvent } from 'react-native';
import Animated from 'react-native-reanimated';
import { runTiming } from './reanimatedHelpers';
import type { State, Config } from './types';
const { Clock, Value } = Animated;
export function useCollapsible(config?: Config) {
const [height, setHeight] = React.useState(0);
const [state, setState] = React.useState<State>(
config?.state === 'expanded' ? 'expanded' : 'collapsed'
);
const { current: clock } = React.useRef(new Clock());
const { current: progress } = React.useRef(new Value<number>(0));
const { current: animation } = React.useRef(new Value<number>(0));
const { current: animatedHeight } = React.useRef(
runTiming(clock, progress, animation, config?.duration, config?.easing)
);
const [mounted, setMounted] = React.useState(
config?.state === 'expanded' ? true : false
);
const handleAnimationEnd = () => {
if (config?.unmountOnCollapse && !config?.show) setMounted(false);
};
React.useEffect(() => {
if (state === 'collapsed') {
animation.setValue(0);
setMounted(false);
handleAnimationEnd();
} else {
animation.setValue(height);
setMounted(true);
}
}, [state, height, animation]);
const onPress = React.useCallback(() => {
setState((prev) => (prev === 'collapsed' ? 'expanded' : 'collapsed'));
}, []);
const onLayout = React.useCallback(
(event: LayoutChangeEvent) => {
const measuredHeight = event.nativeEvent.layout.height;
if (height !== measuredHeight) {
setHeight(measuredHeight);
}
},
[height]
);
return {
onLayout,
onPress,
animatedHeight,
height,
state,
mounted,
setMounted,
};
}
and the runTiming
:
import Animated, { EasingNode } from 'react-native-reanimated';
const { Value, set, cond, startClock, clockRunning, timing, stopClock, block } =
Animated;
export function runTiming(
clock: Animated.Clock,
value: Animated.Value<number>,
dest: Animated.Value<number>,
duration: number = 250,
easing: Animated.EasingNodeFunction = EasingNode.out(EasingNode.ease)
) {
const state = {
finished: new Value(0),
position: value,
time: new Value(0),
frameTime: new Value(0),
};
const config = {
duration,
toValue: dest,
easing,
};
return block([
cond(clockRunning(clock), 0, [
// If the clock isn't running we reset all the animation params and start the clock
set(state.finished, 0),
set(state.time, 0),
set(state.position, value),
set(state.frameTime, 0),
set(config.toValue, dest),
startClock(clock),
]),
// we run the step here that is going to update position
timing(clock, state, config),
// if the animation is over we stop the clock
cond(state.finished, stopClock(clock)),
// return the updated position
state.position,
]);
}
CollapsableView
:
/* eslint-disable react/prop-types */
import React, { useCallback, useEffect, useState } from "react";
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
import Animated, {
Easing,
interpolate,
useAnimatedStyle,
useDerivedValue,
useSharedValue,
withTiming
} from "react-native-reanimated";
import Svg from "react-native-svg";
import { AnimatedSection, useCollapsible } from "reanimated-collapsible-helpers";
import ArrowDownIcon from "@components/CollapsibleView/ArrowDownIcon";
const ROTATE_ANGLE = 180;
const CollapsibleView = ({
children = [],
title = "",
touchableWrapperStyle = {},
initExpanded = false,
expanded = null,
unmountOnCollapse = false,
duration = 200,
arrowStyling = {},
noArrow = false,
activeOpacityFeedback = 0.9,
TouchableComponent = TouchableOpacity,
titleProps = {},
titleStyle = {},
touchableWrapperProps = {},
nested = 0,
lastCommentIndex = -1,
length = 0,
collapsedTitle = "",
isParentLast = false,
parentCommentLength = 0
}) => {
const [show, setShow] = useState<boolean | null | undefined>(initExpanded);
const { animatedHeight, onPress, onLayout, state, mounted, setMounted } =
useCollapsible({
state: initExpanded ? "expanded" : "collapsed",
unmountOnCollapse,
show
});
const rotateAnim = useSharedValue(0);
if (!mounted && expanded) {
setMounted(true);
}
const rotation = useDerivedValue(() => {
return interpolate(rotateAnim.value, [180, 360], [180, 360]);
});
const arrowAnimatedStyle = useAnimatedStyle(() => {
return {
marginTop: 10,
marginHorizontal: 4,
transform: [
{
rotate: `${rotation.value}deg`
}
]
};
});
const handleArrowRotate = useCallback(
(open = null) => {
const _open = open === null ? show : open;
if (!_open) {
rotateAnim.value = withTiming(0, { duration, easing: Easing.ease });
} else {
rotateAnim.value = withTiming(ROTATE_ANGLE, {
duration,
easing: Easing.ease
});
}
},
[duration, rotateAnim, show]
);
const handleToggleShow = () => {
if (!mounted) {
if (!show) {
onPress();
setMounted(true);
}
} else {
onPress();
setShow(!show);
}
};
useEffect(() => {
// this part is to trigger collapsible animation only after he has been fully mounted so animation would
// not be interrupted.
if (mounted) {
setShow(true);
}
}, [mounted]);
useEffect(
() => {
// on mounting set the rotation angle
rotateAnim.value = show ? 0 : ROTATE_ANGLE;
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
useEffect(() => {
if (mounted) {
handleArrowRotate(show);
}
if (show !== expanded) {
setShow(expanded);
}
}, [expanded, handleArrowRotate, mounted, show]);
return (
<View style={styles.container}>
<TouchableComponent
style={[touchableWrapperStyle]}
onPress={handleToggleShow}
activeOpacity={activeOpacityFeedback}
{...touchableWrapperProps}
>
<View
// eslint-disable-next-line react-native/no-inline-styles
style={{
flexDirection: "row",
alignItems: "center",
...titleStyle,
marginLeft: nested === 1 ? 34 : 62
}}
{...titleProps}
>
<View
// eslint-disable-next-line react-native/no-inline-styles
style={{
height: state === "expanded" ? "160%" : "70%",
marginBottom: 12,
borderLeftColor: "#d9d9d9",
borderLeftWidth: nested === 1 ? 2 : 2
}}
/>
{nested > 1 &&
lastCommentIndex > -1 &&
parentCommentLength === 1 &&
!isParentLast ? (
<Svg height="160%" width={2} style={styles.nestedTwoLine} />
) : null}
{nested === 2 &&
parentCommentLength === length - 1 &&
length > 1 &&
!isParentLast ? (
<Svg height="160%" width={2} style={styles.nestedTwoLine} />
) : null}
{nested === 2 && parentCommentLength > 1 && !isParentLast ? (
<Svg height="160%" width={2} style={styles.nestedTwoLine} />
) : null}
{nested === 2 && parentCommentLength > length && length > 1 && !isParentLast ? (
<Svg height="160%" width={2} style={styles.nestedTwoLine} />
) : null}
{nested === 2 && length > 1 && parentCommentLength > length && !isParentLast ? (
<Svg height="160%" width={2} style={styles.nestedTwoLine} />
) : null}
{state !== "expanded" ? (
<Svg height={2} width={10} style={styles.horizontalLine} />
) : null}
{noArrow ? null : (
<Animated.View style={arrowAnimatedStyle}>
<ArrowDownIcon {...arrowStyling} />
</Animated.View>
)}
<Text style={styles.title}>
{state === "collapsed" ? title : collapsedTitle}
</Text>
</View>
</TouchableComponent>
{mounted ? (
<AnimatedSection
animatedHeight={animatedHeight}
onLayout={onLayout}
state={state}
style={styles.animatedSection}
>
{children}
</AnimatedSection>
) : null}
</View>
);
};
export default CollapsibleView;
const styles = StyleSheet.create({
container: {
flex: 1
},
title: {
color: "#9C9C9C",
fontSize: 16,
marginTop: 10
},
nestedTwoLine: {
marginBottom: 10,
backgroundColor: "#d9d9d9",
position: "absolute",
left: -28
},
horizontalLine: {
backgroundColor: "#d9d9d9",
marginTop: 10,
marginLeft: -2
},
animatedSection: {
width: "100%"
}
});
This used to work fine on 2.14.x
Steps to reproduce
code sample above
Snack or a link to a repository
none
Reanimated version
3.0.0-rc.10
React Native version
0.71.0
Platforms
Android, iOS
JavaScript runtime
Hermes
Workflow
React Native (without Expo)
Architecture
Paper (Old Architecture)
Build type
Debug mode
Device
iOS simulator
Device model
No response
Acknowledgements
Yes
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 16 (6 by maintainers)
This may not be the exact same issue but we found that we had missed a dependency that needed to be updated -
react-native-tab-view
in our case, see https://github.com/software-mansion/react-native-reanimated/issues/4420#issuecomment-1734857719@piaskowyk thanks for the clarification! I would love some help on how to transition since I do not know what are equivalent replacements of these functions.
This is actually not a bug. In last year we dropped the support for Reanimated 1 and in Reanimated 3 we removed source code of Reanimated 1 from package. Reanimated 2 still contains API v1.
I recommend to rewrite parts with v1 API to v2 API. If you want some guidance I can help you.