Reputation: 116
I am trying to create a youtube type of video player app(for learning purposes only) with expo and react native.
But I have this very weird issue that each time I save file and hot reload starts it throws error like
RangeError: Maximum call stack size exceeded
I have already looked about this and found that this error is occurring because of recursion. But I can't find any recursion in my code.
But more weird is that first time app runs fine and when I change something it shows this error and app crashes. But if I save the same file without any changes it refreshes and works normally. so it is like each even time I save it shows error and next time I save file it goes away.
I am using expo go app to test this app on my physical device.
Exact code of file is this:
import React, { useRef, useEffect, useState } from "react";
import { StyleSheet, Text, View, Dimensions, Pressable } from "react-native";
import { Video } from "expo-av";
/** config.js contains this values
export const theme = {
backgroundColor: "#f1f1f1",
fontColor: "#212121",
};
*/
import { theme } from "../config";
import { PanGestureHandler } from "react-native-gesture-handler";
import Animated, {
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
withTiming,
interpolate,
runOnJS,
} from "react-native-reanimated";
import * as MediaLibrary from "expo-media-library";
const AnimatedVideo = Animated.createAnimatedComponent(Video);
const { width, height } = Dimensions.get("window");
const BAR_HEIGHT = 60;
const BREAK_POINT = 100;
const Videos = () => {
const video = useRef(null);
const [status, setStatus] = useState({});
// useEffect(() => {
// getVideos();
// }, []);
// const getVideos = async () => {
// try {
// const { status } = await MediaLibrary.requestPermissionsAsync();
// if (status === "granted") {
// const userVideos = await MediaLibrary.getAssetsAsync({
// first: 999,
// mediaType: MediaLibrary.MediaType.video,
// });
// }
// } catch (error) {
// console.log(error);
// }
// };
const translateY = useSharedValue(0);
const offsetY = useSharedValue(0);
const state = useSharedValue("down");
const onGestureEvent = useAnimatedGestureHandler({
onActive: ({ translationY }) => {
translateY.value = translationY + offsetY.value;
},
onEnd: ({ translationY, velocityY }) => {
const point = translationY + 0.2 * velocityY;
if (state.value === "down") {
if (point > 0) {
// Go down rather close the player
translateY.value = withTiming(BAR_HEIGHT);
offsetY.value = BAR_HEIGHT;
} else {
// Go up
translateY.value = withTiming(-(height - BAR_HEIGHT));
offsetY.value = -(height - BAR_HEIGHT);
state.value = "up";
}
} else if (state.value === "up") {
if (point > 0) {
// Go down
translateY.value = withTiming(0);
offsetY.value = 0;
state.value = "down";
} else {
// Go Full Screen
translateY.value = withTiming(-(height - BAR_HEIGHT));
offsetY.value = -(height - BAR_HEIGHT);
state.value = "full";
}
}
},
});
const videoContStyle = useAnimatedStyle(() => {
return {
width,
height,
backgroundColor: "white",
position: "absolute",
zIndex: 2,
top: 0,
transform: [{ translateY: height - BAR_HEIGHT + translateY.value }],
};
});
const playerContStyle = useAnimatedStyle(() => {
return {
height: interpolate(
translateY.value,
[-(height - BAR_HEIGHT), 0],
[225, BAR_HEIGHT]
),
width: "100%",
borderWidth: StyleSheet.hairlineWidth,
borderColor: "black",
};
});
const videoStyle = useAnimatedStyle(() => {
return {
width:
translateY.value < -BREAK_POINT
? "100%"
: interpolate(
translateY.value,
[-BREAK_POINT, 0],
[width, width / 2]
),
height: "100%",
};
});
return (
<View style={styles.container}>
<View style={styles.mainCont}>
<Pressable
onPress={() => {
translateY.value = withTiming(-(height - BAR_HEIGHT));
offsetY.value = -(height - BAR_HEIGHT);
state.value = "up";
}}
>
<Text>Go full screen</Text>
</Pressable>
</View>
<Animated.View style={videoContStyle}>
<View style={styles.gestureCont}>
<PanGestureHandler onGestureEvent={onGestureEvent}>
<Animated.View style={playerContStyle}>
<AnimatedVideo
ref={video}
style={videoStyle}
source={{
uri: "file:///storage/emulated/0/Download/bannerg004.mp4",
}}
useNativeControls={true}
resizeMode="cover"
onPlaybackStatusUpdate={(newstatus) => setStatus(newstatus)}
/>
</Animated.View>
</PanGestureHandler>
</View>
<Pressable
onPress={() => {
status.isPlaying
? video.current.pauseAsync()
: video.current.playAsync();
}}
>
<Text>{status.isPlaying ? "pause" : "Play"}</Text>
</Pressable>
</Animated.View>
</View>
);
};
export default Videos;
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
backgroundColor: theme.backgroundColor,
},
mainCont: {
width,
height,
},
videoCont: {
width,
height,
backgroundColor: "lightblue",
position: "absolute",
zIndex: 2,
top: 0,
transform: [{ translateY: height - BAR_HEIGHT }],
},
playerCont: {
height: BAR_HEIGHT,
width: "100%",
borderWidth: StyleSheet.hairlineWidth,
borderColor: "black",
},
});
Platform details:
I have tried bunch of things like:
I have found that this issues generally happens either becuase of useEffect hook or because of chain rendering. But none of them is valid for this one.
Please, Can someone suggest what I am doing wrong?
Upvotes: 1
Views: 1471
Reputation: 116
Simple solution for this is to use Animated.View component wrapped around Video component for your required animated styling.
But still I have no luck finding the real problems solution.
Upvotes: 1