I'm fairly new to react-native and decided to try cloning an app, following a tutorial. I'm trying to pass data stored in redux to a map rendered on my home screen but when I run, I keep getting the following error: null is not an object (evaluating origin.location). Any suggestions, solutions and quick fixes are welcome. Thanks
Home screen codes
import { View, TouchableOpacity, Text } from 'react-native'
import { React } from 'react'
import HeaderTabs from '../components/HeaderTabs'
import { GooglePlacesAutocomplete } from 'react-native-google-places-autocomplete'
import { Ionicons } from '@expo/vector-icons'
import { setDestination, setOrigin } from '../slices/navSlice'
import { useDispatch } from 'react-redux'
import { SafeAreaView } from 'react-native-safe-area-context'
import { GOOGLE_MAPS_APIKEY } from '@env'
import Map from './components/Map'
const Home = ({navigation}) =>{
const dispatch = useDispatch();
return (
<SafeAreaView style={{ backgroundColor:"white",
flex:1,
padding:5,
marginTop:-20
}}>
<View style={{alignItems:"flex-end"}} >
<TouchableOpacity onPress={() => {navigation.navigate("CustomerInfo")}}>
<Ionicons name="person-circle" size={30} color="black" />
</TouchableOpacity>
</View>
<HeaderTabs />
<View style={{marginTop:15, flexDirection:"row"}}>
<GooglePlacesAutocomplete
query={{key: GOOGLE_MAPS_APIKEY}}
enablePoweredByContainer={false}
onPress={(data, details = null) => {
dispatch(setOrigin({
location: details.geometry.location,
description: data.description,
})
);
dispatch(setDestination(null));
}}
fetchDetails={true}
returnKeyType={"search"}
placeholder='Enter package pickup location'
debounce={400}
nearbyPlacesAPI='GooglePlacesSearch'
styles={{
textInput:{
backgroundColor:"#eee",
borderRadius:15,
fontFamily:"Poppins_500Medium",
marginTop:10,
fontSize:18,
},
textInputContainer:{
backgroundColor:"#eee",
borderRadius:15,
flexDirection:"row",
alignItems:"center",
width:380
},
}}
renderLeftButton={() => (
<View style={{
marginLeft:10
}}>
<Ionicons name="location" size={20} color="green" />
</View>
)}
renderRightButton={() => (
<View style={{
flexDirection:"row",
marginRight:5,
backgroundColor:"white",
padding:9,
borderRadius:30,
alignItems:"center"
}}>
<Ionicons name="search" size={15} color="black" />
</View>
)}
/>
</View>
<Map />
</SafeAreaView>
)};
export default Home;
Map component codes
import { View, Text } from 'react-native'
import React from 'react'
import MapView, { Marker } from 'react-native-maps'
import { useSelector } from 'react-redux'
import { selectOrigin } from '../slices/navSlice'
const Map = () => {
const origin = useSelector(selectOrigin);
return (
<MapView
style={{flex:1, marginTop:10}}
initialRegion={{
latitude: origin.location.lat,
longitude: origin.location.lng,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
/>
);
};
export default Map;
Where origin.location is coming from
const initialState = {
origin: null,
destination: null,
travelTimeInformation: null
}
export const navSlice = createSlice({
name: "nav",
initialState,
reducer: {
setOrigin: (state, action) => {
state.origin = action.payload;
},
setDestination: (state, action) => {
state.destination = action.payload;
},
setTravelTimeInformation: (state, action) => {
state.travelTimeInformation = action.payload;
},
},
});
export const { setOrigin, setDestination, setTravelTimeInformation } = navSlice.actions;
export const selectOrigin = (state) => state.nav.origin;
export const selectDestination = (state) => state.nav.destination;
export const selectTravelTimeInformation = (state) => state.nav.travelTimeInformation;
export default navSlice.reducer;
CodePudding user response:
Issue
The origin
state value is initially null
.
const initialState = {
origin: null, // <-- initially null here
destination: null,
travelTimeInformation: null
}
And the code is attempting to access into this null value.
const Map = () => {
const origin = useSelector(selectOrigin);
return (
<MapView
style={{flex:1, marginTop:10}}
initialRegion={{
latitude: origin.location.lat,
longitude: origin.location.lng,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
/>
);
};
Solutions
You've a few options. Here are 4, there may be others.
Conditionally render the
MapView
component on theorigin
state being populated.const Map = () => { const origin = useSelector(selectOrigin); return origin?.location ? ( <MapView style={{flex:1, marginTop:10}} initialRegion={{ latitude: origin.location.lat, longitude: origin.location.lng, latitudeDelta: 0.0922, longitudeDelta: 0.0421, }} /> ) : null; };
Use Optional Chaining operator on the
origin
access and provide a fallback value.const Map = () => { const origin = useSelector(selectOrigin); return ( <MapView style={{flex:1, marginTop:10}} initialRegion={{ latitude: origin?.location?.lat ?? 0, // use fallback value that makes sense longitude: origin?.location?.lng ?? 0, // use fallback value that makes sense latitudeDelta: 0.0922, longitudeDelta: 0.0421, }} /> ); };
Provide a valid, defined origin value from the selector.
const defaultOrigin = { location: { lat: 0, lng: 0 } }; // use origin value that makes sense export const selectOrigin = (state) => state.nav.origin ?? defaultOrigin;
Or just provide a more useful initial state value.
const defaultOrigin = { location: { lat: 0, lng: 0 } }; // use origin value that makes sense const initialState = { origin: defaultOrigin, destination: null, travelTimeInformation: null }