首页 > 解决方案 > RangeError:本机反应超出了最大调用堆栈大小与expo

问题描述

我正在尝试使用 expo 创建一个 youtube 类型的视频播放器应用程序(仅用于学习目的)并做出原生反应。但是我有一个非常奇怪的问题,每次我保存文件并开始热重载时都会抛出类似的错误 RangeError: Maximum call stack size exceeded

我已经看过这个,发现这个错误是由于递归而发生的。但我在我的代码中找不到任何递归。

但更奇怪的是,第一次应用程序运行良好,当我更改某些内容时,它会显示此错误并且应用程序崩溃。但是,如果我保存相同的文件而不进行任何更改,它会刷新并正常工作。所以就像每次我保存它都会显示错误,而下次我保存文件时它就会消失。

我正在使用 expo go app 在我的物理设备上测试这个应用程序。

文件的确切代码是这样的:

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",
  },
});

平台详情:

我尝试了很多事情,例如:

  1. 从 PanGestureHandler 中删除 onGestureEvent 但有同样的问题。
  2. 从 AnimatedVideo 中删除 onPlaybackStatusUpdate 道具但有同样的问题。

我发现这个问题通常是由于 useEffect 钩子或由于链渲染而发生的。但没有一个对这个有效。

请,有人可以建议我做错了什么吗?

标签: javascriptandroidreact-nativeexpo

解决方案


对此的简单解决方案是使用包裹在 Video 组件周围的 Animated.View 组件来实现所需的动画样式。

但是我仍然没有运气找到真正的问题解决方案。


推荐阅读