ReactNativeでExpoを使ったアプリの開発をしています。
アプリの簡単な内容としてはアプリからユーザが位置情報を持った投稿をfirestoreに投稿し
他ユーザはマップの中心近くの投稿を検索することでMapViewにマーカーが立ち、投稿を見ることができるというものです。
エラー詳細は
Error
1RangeError:maximum call stack size exceeded 2This error is located at: 3in Image(at MapScreen.js) 4in AIRMapMarker(at MapMarker.js) 5in MapMarker(at MapScreen.js)
と出ているとおりImageコンポーネントでエラーが起きていると思っているのですが原因がわかりません。
RangeError:maximum call stack size exceeded というエラーからして再帰処理が起きていると思うのですが、どこで起きているのか分からず、疑い深い部分としてはsetStateすると再レンダリングがかかるらしく、stateが関係しているのではと思っています。
質問、お時間ある方よろしくお願いします。
追記(エラーが起きる動作を書いていませんでした)
アプリ起動時componentDidMountのgetLocationAsyncを呼び出し、getLocationAsync内のgetMusicで、そこから画面の中心近くの投稿をfirestoreから取得しmapにmarkerを表示するのですが、
Mapを動かして中心を変更した後、再検索しようと再度getMusic関数を呼び出すと上記のエラーが起きます。
MapScreen
1import React from "react"; 2import styled from "styled-components"; 3import { 4 View, 5 Text, 6 SafeAreaView, 7 StatusBar, 8 AsyncStorage, 9 Image, 10} from "react-native"; 11import { SearchBar } from "react-native-elements"; 12import MapView from "react-native-maps"; 13import { Callout, Marker } from "react-native-maps"; 14import * as Location from "expo-location"; 15import * as Permissions from "expo-permissions"; 16import { TouchableOpacity } from "react-native-gesture-handler"; 17import { Ionicons } from "@expo/vector-icons"; 18import { connect } from "react-redux"; 19import SoundLocation from "../components/Location"; 20import firebase from "../components/Firebase"; 21import { GeoFirestore } from "geofirestore"; 22 23function mapStateToProps(state) { 24 return { action: state.action }; 25} 26 27function mapDispatchToProps(dispatch) { 28 return {}; 29} 30 31const items = []; 32const firestore = firebase.firestore(); 33const geofirestore = new GeoFirestore(firestore); 34const geocollection = geofirestore.collection("posts"); 35 36class MapScreen extends React.Component { 37 static navigationOptions = { 38 header: null, 39 }; 40 41 constructor(props) { 42 super(props); 43 this.state = { 44 latitude: null, 45 longitude: null, 46 region: { 47 latitude: null, 48 longitude: null, 49 latitudeDelta: 0.02, 50 longitudeDelta: 0.02, 51 }, 52 firstQuery: "", 53 message: "位置情報取得中", 54 display: "none", 55 items: [], 56 }; 57 this.onRegionChangeComplete = this.onRegionChangeComplete.bind(this); 58 } 59 60 componentDidMount() { 61 StatusBar.setBarStyle("dark-content", true); 62 this.retrieveName(); 63 this.getLocationAsync(); 64 } 65 66 componentDidUpdate() { 67 68 } 69 70 retrieveName = async () => { 71 try { 72 const email = await AsyncStorage.getItem("email"); 73 if (email !== null) { 74 this.setState({ 75 display: "inline", 76 }); 77 } else { 78 this.setState({ 79 display: "none", 80 }); 81 } 82 } catch (error) {} 83 }; 84 85 getLocationAsync = async () => { 86 const { status } = await Permissions.askAsync(Permissions.LOCATION); 87 if (status !== "granted") { 88 this.setState({ 89 message: "位置情報のパーミッションの取得に失敗しました。", 90 }); 91 return; 92 } 93 let location = await Location.getCurrentPositionAsync({}); 94 var new_state = { 95 latitude: location.coords.latitude, 96 longitude: location.coords.longitude, 97 latitudeDelta: 0.02, 98 longitudeDelta: 0.02, 99 }; 100 this.setState({ 101 latitude: location.coords.latitude, 102 longitude: location.coords.longitude, 103 region: new_state, 104 }); 105 this.getMusic(); 106 }; 107 108 //検索 109 getMusic = function () { 110 const query = geocollection.near({ 111 center: new firebase.firestore.GeoPoint( 112 this.state.region.latitude, 113 this.state.region.longitude 114 ), 115 //delta * 100のあたい(緯度一度は約111kmらしい) 116 radius: this.state.region.latitudeDelta * 100, 117 }); 118 query.get().then((values) => { 119 items.length = 0; 120 values.docs.forEach(function (value, index) { 121 //itemsに格納する処理 122 console.log(value.data().title); 123 items.push({ 124 latitude: value.data().coordinates._lat, 125 longitude: value.data().coordinates._long, 126 icon: value.data().icon, 127 url: value.data().URL, 128 artist: value.data().artist, 129 title: value.data().title, 130 }); 131 }); 132 this.setState( 133 { 134 items: items, 135 }, 136 function () { 137 //console.log(this.state.items); 138 } 139 ); 140 }); 141 }; 142 143 getTest = () => { 144 //console.log(this.map.context); 145 this.map.animateToRegion( 146 { 147 latitude: this.state.latitude, 148 longitude: this.state.longitude, 149 latitudeDelta: 0.02, //小さくなるほどズーム 150 longitudeDelta: 0.02, 151 }, 152 1000 153 ); 154 }; 155 156 _onPressButton = async function (url) { 157 //省略 158 }; 159 160 onPlaybackStatusUpdate = (status) => { 161 //省略 162 }; 163 164 onRegionChangeComplete(region) { 165 var new_state = { 166 latitude: region.latitude, 167 longitude: region.longitude, 168 latitudeDelta: region.latitudeDelta, 169 longitudeDelta: region.longitudeDelta, 170 }; 171 this.setState({ 172 region: new_state, 173 }); 174 } 175 176 render() { 177 if (this.state.latitude && this.state.longitude) { 178 return ( 179 <RootView> 180 <View> 181 <SafeAreaView 182 style={{ 183 backgroundColor: "yellow", 184 display: "flex", 185 flexDirection: "row", 186 justifyContent: "space-between", 187 alignItems: "center", 188 }} 189 > 190 <SearchBar 191 round 192 onClearText={(text) => console.log(text)} 193 onChangeText={(query) => { 194 this.setState({ firstQuery: query }); 195 }} 196 returnKeyType="search" 197 value={this.state.firstQuery} 198 onEndEditing={() => console.log(this.state.firstQuery)} 199 containerStyle={{ 200 width: "80%", 201 borderRadius: 20, 202 marginBottom: 5, 203 marginLeft: 10, 204 }} 205 /> 206 <View 207 style={{ 208 flex: 1, 209 position: "absolute", 210 top: 50, 211 right: 20, 212 }} 213 > 214 <TouchableOpacity onPress={() => this.getMusic()}> 215 <Ionicons name="md-wifi" size={32} color="black" /> 216 </TouchableOpacity> 217 </View> 218 </SafeAreaView> 219 </View> 220 <MapView 221 ref={(map) => { 222 this.map = map; 223 }} 224 style={{ flex: 1 }} 225 initialRegion={{ 226 latitude: this.state.latitude, 227 longitude: this.state.longitude, 228 latitudeDelta: 0.02, //小さくなるほどズーム 229 longitudeDelta: 0.02, 230 }} 231 onRegionChangeComplete={(region) => 232 this.onRegionChangeComplete(region) 233 } 234 showsUserLocation={true} 235 showsMyLocationButton={true} 236 > 237 {this.state.items.map((item, index) => ( 238 <Marker 239 key={index} 240 coordinate={{ 241 latitude: item.latitude, 242 longitude: item.longitude, 243 }} 244 > 245 <Image 246 source={{ uri: item.icon }} 247 resizeMode="contain" 248 style={{ width: 44, height: 44 }} 249 /> 250 <Callout onPress={() => this._onPressButton(item.url)}> 251 <Location 252 key={index} 253 image={require("../assets/background.jpg")} 254 title={item.title} 255 artist={item.artist} 256 logo={{ uri: item.icon }} 257 author="author" 258 avatar={require("../assets/avatar.jpg")} 259 caption="caption" 260 /> 261 </Callout> 262 </Marker> 263 ))} 264 </MapView> 265 <View 266 style={{ 267 flex: 1, 268 position: "absolute", 269 bottom: 30, 270 left: 30, 271 }} 272 > 273 <TouchableOpacity onPress={() => this.getTest()}> 274 <Ionicons name="ios-navigate" size={48} color="black" /> 275 </TouchableOpacity> 276 </View> 277 <View 278 style={{ 279 flex: 1, 280 position: "absolute", 281 bottom: 30, 282 right: 30, 283 }} 284 > 285 <TouchableOpacity 286 onPress={() => { 287 this.props.navigation.push("Modal"); 288 }} 289 > 290 <Test source={require("../assets/avatar.jpg")} /> 291 </TouchableOpacity> 292 </View> 293 </RootView> 294 ); 295 } 296 return ( 297 <View style={{ flex: 1, justifyContent: "center" }}> 298 <Text>{this.state.message}</Text> 299 </View> 300 ); 301 } 302} 303 304export default connect(mapStateToProps, mapDispatchToProps)(MapScreen); 305 306const RootView = styled.View` 307 flex: 1; 308`; 309 310const Test = styled.Image` 311 width: 44px; 312 height: 44px; 313 background: black; 314 border-radius: 22px; 315 margin-left: 20px; 316`; 317
回答1件
あなたの回答
tips
プレビュー