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 likeRangeError: 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:
- Expo sdk: 40.0
- android device: Samsung galaxy j7 neo(physical device) android 9.0
- OS: windows 7
I have tried bunch of things like:
- removing onGestureEvent from PanGestureHandler but had the same problem.
- removing onPlaybackStatusUpdate prop from AnimatedVideo but had the same issue.
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?