質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.92%

ReactNative maximum call stack size exceeded の原因

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 451

rashild

score 17

ReactNativeでExpoを使ったアプリの開発をしています。
アプリの簡単な内容としてはアプリからユーザが位置情報を持った投稿をfirestoreに投稿し
他ユーザはマップの中心近くの投稿を検索することでMapViewにマーカーが立ち、投稿を見ることができるというものです。
エラー詳細は

RangeError:maximum call stack size exceeded 
This error is located at:
in Image(at MapScreen.js)
in AIRMapMarker(at MapMarker.js)
in MapMarker(at MapScreen.js)


と出ているとおりImageコンポーネントでエラーが起きていると思っているのですが原因がわかりません。
RangeError:maximum call stack size exceeded というエラーからして再帰処理が起きていると思うのですが、どこで起きているのか分からず、疑い深い部分としてはsetStateすると再レンダリングがかかるらしく、stateが関係しているのではと思っています。

質問、お時間ある方よろしくお願いします。

追記(エラーが起きる動作を書いていませんでした)
アプリ起動時componentDidMountのgetLocationAsyncを呼び出し、getLocationAsync内のgetMusicで、そこから画面の中心近くの投稿をfirestoreから取得しmapにmarkerを表示するのですが、
Mapを動かして中心を変更した後、再検索しようと再度getMusic関数を呼び出すと上記のエラーが起きます。

import React from "react";
import styled from "styled-components";
import {
  View,
  Text,
  SafeAreaView,
  StatusBar,
  AsyncStorage,
  Image,
} from "react-native";
import { SearchBar } from "react-native-elements";
import MapView from "react-native-maps";
import { Callout, Marker } from "react-native-maps";
import * as Location from "expo-location";
import * as Permissions from "expo-permissions";
import { TouchableOpacity } from "react-native-gesture-handler";
import { Ionicons } from "@expo/vector-icons";
import { connect } from "react-redux";
import SoundLocation from "../components/Location";
import firebase from "../components/Firebase";
import { GeoFirestore } from "geofirestore";

function mapStateToProps(state) {
  return { action: state.action };
}

function mapDispatchToProps(dispatch) {
  return {};
}

const items = [];
const firestore = firebase.firestore();
const geofirestore = new GeoFirestore(firestore);
const geocollection = geofirestore.collection("posts");

class MapScreen extends React.Component {
  static navigationOptions = {
    header: null,
  };

  constructor(props) {
    super(props);
    this.state = {
      latitude: null,
      longitude: null,
      region: {
        latitude: null,
        longitude: null,
        latitudeDelta: 0.02,
        longitudeDelta: 0.02,
      },
      firstQuery: "",
      message: "位置情報取得中",
      display: "none",
      items: [],
    };
    this.onRegionChangeComplete = this.onRegionChangeComplete.bind(this);
  }

  componentDidMount() {
    StatusBar.setBarStyle("dark-content", true);
    this.retrieveName();
    this.getLocationAsync();
  }

  componentDidUpdate() {

  }

  retrieveName = async () => {
    try {
      const email = await AsyncStorage.getItem("email");
      if (email !== null) {
        this.setState({
          display: "inline",
        });
      } else {
        this.setState({
          display: "none",
        });
      }
    } catch (error) {}
  };

  getLocationAsync = async () => {
    const { status } = await Permissions.askAsync(Permissions.LOCATION);
    if (status !== "granted") {
      this.setState({
        message: "位置情報のパーミッションの取得に失敗しました。",
      });
      return;
    }
    let location = await Location.getCurrentPositionAsync({});
    var new_state = {
      latitude: location.coords.latitude,
      longitude: location.coords.longitude,
      latitudeDelta: 0.02,
      longitudeDelta: 0.02,
    };
    this.setState({
      latitude: location.coords.latitude,
      longitude: location.coords.longitude,
      region: new_state,
    });
    this.getMusic();
  };

  //検索
  getMusic = function () {
    const query = geocollection.near({
      center: new firebase.firestore.GeoPoint(
        this.state.region.latitude,
        this.state.region.longitude
      ),
      //delta * 100のあたい(緯度一度は約111kmらしい)
      radius: this.state.region.latitudeDelta * 100,
    });
    query.get().then((values) => {
      items.length = 0;
      values.docs.forEach(function (value, index) {
        //itemsに格納する処理
        console.log(value.data().title);
        items.push({
          latitude: value.data().coordinates._lat,
          longitude: value.data().coordinates._long,
          icon: value.data().icon,
          url: value.data().URL,
          artist: value.data().artist,
          title: value.data().title,
        });
      });
      this.setState(
        {
          items: items,
        },
        function () {
          //console.log(this.state.items);
        }
      );
    });
  };

  getTest = () => {
    //console.log(this.map.context);
    this.map.animateToRegion(
      {
        latitude: this.state.latitude,
        longitude: this.state.longitude,
        latitudeDelta: 0.02, //小さくなるほどズーム
        longitudeDelta: 0.02,
      },
      1000
    );
  };

  _onPressButton = async function (url) {
    //省略
  };

  onPlaybackStatusUpdate = (status) => {
  //省略
  };

  onRegionChangeComplete(region) {
    var new_state = {
      latitude: region.latitude,
      longitude: region.longitude,
      latitudeDelta: region.latitudeDelta,
      longitudeDelta: region.longitudeDelta,
    };
    this.setState({
      region: new_state,
    });
  }

  render() {
    if (this.state.latitude && this.state.longitude) {
      return (
        <RootView>
          <View>
            <SafeAreaView
              style={{
                backgroundColor: "yellow",
                display: "flex",
                flexDirection: "row",
                justifyContent: "space-between",
                alignItems: "center",
              }}
            >
              <SearchBar
                round
                onClearText={(text) => console.log(text)}
                onChangeText={(query) => {
                  this.setState({ firstQuery: query });
                }}
                returnKeyType="search"
                value={this.state.firstQuery}
                onEndEditing={() => console.log(this.state.firstQuery)}
                containerStyle={{
                  width: "80%",
                  borderRadius: 20,
                  marginBottom: 5,
                  marginLeft: 10,
                }}
              />
              <View
                style={{
                  flex: 1,
                  position: "absolute",
                  top: 50,
                  right: 20,
                }}
              >
                <TouchableOpacity onPress={() => this.getMusic()}>
                  <Ionicons name="md-wifi" size={32} color="black" />
                </TouchableOpacity>
              </View>
            </SafeAreaView>
          </View>
          <MapView
            ref={(map) => {
              this.map = map;
            }}
            style={{ flex: 1 }}
            initialRegion={{
              latitude: this.state.latitude,
              longitude: this.state.longitude,
              latitudeDelta: 0.02, //小さくなるほどズーム
              longitudeDelta: 0.02,
            }}
            onRegionChangeComplete={(region) =>
              this.onRegionChangeComplete(region)
            }
            showsUserLocation={true}
            showsMyLocationButton={true}
          >
            {this.state.items.map((item, index) => (
              <Marker
                key={index}
                coordinate={{
                  latitude: item.latitude,
                  longitude: item.longitude,
                }}
              >
                <Image
                  source={{ uri: item.icon }}
                  resizeMode="contain"
                  style={{ width: 44, height: 44 }}
                />
                <Callout onPress={() => this._onPressButton(item.url)}>
                  <Location
                    key={index}
                    image={require("../assets/background.jpg")}
                    title={item.title}
                    artist={item.artist}
                    logo={{ uri: item.icon }}
                    author="author"
                    avatar={require("../assets/avatar.jpg")}
                    caption="caption"
                  />
                </Callout>
              </Marker>
            ))}
          </MapView>
          <View
            style={{
              flex: 1,
              position: "absolute",
              bottom: 30,
              left: 30,
            }}
          >
            <TouchableOpacity onPress={() => this.getTest()}>
              <Ionicons name="ios-navigate" size={48} color="black" />
            </TouchableOpacity>
          </View>
          <View
            style={{
              flex: 1,
              position: "absolute",
              bottom: 30,
              right: 30,
            }}
          >
            <TouchableOpacity
              onPress={() => {
                this.props.navigation.push("Modal");
              }}
            >
              <Test source={require("../assets/avatar.jpg")} />
            </TouchableOpacity>
          </View>
        </RootView>
      );
    }
    return (
      <View style={{ flex: 1, justifyContent: "center" }}>
        <Text>{this.state.message}</Text>
      </View>
    );
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(MapScreen);

const RootView = styled.View`
  flex: 1;
`;

const Test = styled.Image`
  width: 44px;
  height: 44px;
  background: black;
  border-radius: 22px;
  margin-left: 20px;
`;
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • hoshi-takanori

    2020/04/10 04:28

    これでしょうか?
    https://stackoverflow.com/a/57424511/1019868

    キャンセル

  • rashild

    2020/04/11 00:06

    今回の質問とは関係なさそうです。
    エラー内容は変わっていませんでした。
    ありがとうございます

    キャンセル

回答 1

check解決した方法

0

すみません、自己解決しました。
FirestoreのGeoPointを手動で操作したせいで、GeoFirestoreが上手く動作していなかったようでデータを作り直したら正常に動作しました。
しかし、hoshi-takanoriさんの提案も起動時に何度も呼び出されている原因がわかっていなかったので大変助かりました。
ありがとうございました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.92%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る