Redux State Problem : TypeError: undefined is not an object

I am new to react native and redux especialy and I got an error message that I really struggle to deal with.

This is the error im getting

TypeError: undefined is not an object (evaluating '(0, _reactRedux.useSelector)(function (state) { return state.myPigeons.myPigeons; }).length')

I will show you here all my related code I got so far and after that explain what my intention behind all this is.

MainScreen.js

import React, {useState, useEffect} from 'react';
import {View, Text, StyleSheet, Button} from 'react-native'; 
import { useDispatch, useSelector } from 'react-redux';
import CustomButton from '../components/CustomButton';
import CustomPigeonPicker from '../components/CustomPigeonsPicker';
import { addPigeon } from '../store/actions/pigeon';


const MainScreen = props =>{
    dispatch = useDispatch();
    const availablePigeons = (useSelector(state => state.myPigeons.myPigeons)).length;
    return(
        <View style={styles.screen}>
            <View style={styles.tophalf}>
                <CustomPigeonPicker style={styles.pigeonpicker}
                    placeholder={`You have so many pigeons: ${availablePigeons}`}
                />
            </View>
            <View style={styles.bottomhalf}>
                <CustomButton 
                    style={styles.button} 
                    onPress={() => dispatch(addPigeon)}
                />
            </View>
        </View>
    )
};

const styles = StyleSheet.create({
    screen:{
        flexDirection: "column",
        flex: 1
    },
    button:{
        fontFamily: "AmaticBold",
        //Ab hier Einstellungen zum Schatten
        shadowColor: "#000",
        shadowOffset: {
            width: 0,
            height: 5,
        },
        shadowOpacity: 0.34,
        shadowRadius: 6,
        elevation: 3.5,
        width: "30%",
    },
    tophalf:{
        flex: 1,
        alignItems: "center"
    },
    bottomhalf:{
        flex:1,
        alignItems: "center"
    },
    pigeonpicker:{
    
    }
});

export default MainScreen;

pigeon.js (action)

export const ADD_PIGEON = 'ADD_PIGEON';

export const addPigeon = () => {
    return {type: ADD_PIGEON}
};

pigeon.js (reducer)

import {ALLPIGEONS} from '../../data/pigeons_data';
import { ADD_PIGEON } from '../actions/pigeon';


const initialPigeonState = () => {
    myPigeons = []
};

const pigeonReducer = (state = initialPigeonState, action) => {
    switch(action.type){
        case ADD_PIGEON:{
            var generatedPigeon = Math.floor(Math.random() * ALLPIGEONS.length);
            generatedPigeon.nickname = "Kuba";
            var updatedPigeons = [...state.myPigeons, generatedPigeon]
            return{...state, myPigeons: updatedPigeons}
        }
    };
    return state;
};

export default pigeonReducer;

pigeons_data.js

import pigeon from '../models/pigeon';

const ALLPIGEONS = [
    new pigeon(
        1,
        "red",
        "Red-billed pigeon",
        " "
    ),
    new pigeon(
        2,
        "blue",
        "Blue pigeon",
        " "
    ),
    new pigeon(
        3,
        "white",
        "Release dove",
        " "
    ),
    new pigeon(
        4,
        "brown",
        "Brown cuckoo-dove",
        " "
    ),
    new pigeon(
        5,
        "green",
        "Green pigeon",
        " "
    ),
];

export default ALLPIGEONS;

App.js

//Automatic imports
import { StatusBar } from 'expo-status-bar';
import React, {useState} from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Provider } from 'react-redux';

//My imports
import * as Font from 'expo-font';
import {AppLoading} from 'expo';
import ReduxThunk from 'redux-thunk';
import { createStore, combineReducers, applyMiddleware } from 'redux';

//import other screens
import PBNavigator from './navigation/PBNavigator';

//import Reducers
import authReducer from './store/reducers/auth';
import pigeonReducer from './store/reducers/pigeon';

//Loading Fonts, returns promise
const fetchFonts = () => {
  return Font.loadAsync({
    'Magnus' : require('./assets/fonts/MagnusText.ttf'),
    'AmaticBold' : require('./assets/fonts/Amatic-Bold.ttf'),
    'AmaticRegular' : require('./assets/fonts/AmaticSC-Regular.ttf'),
    'SEASRN' : require('./assets/fonts/SEASRN.ttf'),
  });
};

const rootReducer = combineReducers({
  auth: authReducer,
  myPigeons: pigeonReducer,
});

const store = createStore(rootReducer, applyMiddleware(ReduxThunk));

export default function App() {
  const [dataLoaded, setDataLoaded] = useState(false); //are fonts loaded?

  if(!dataLoaded){ //will go into if clause because fonts are not loaded
    return(
      <AppLoading 
        startAsync={fetchFonts} 
        onFinish={() => setDataLoaded(true)}
        onError={(err) => console.log(err)}
      />
    )
  }
  return (
    <Provider store={store}>
      <PBNavigator/>
    </Provider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

The problem here is basicly that I want to have a state in redux where all my pigeons I have are stored (my state should be empty right now because I did not add any pigeon to my state) and after I click the button in Mainscreen.js I add a pigeon to the state so that the text can display how many pigeons I have.

Right now the app is always crashing when I make my way to the MainScreen. I am thankfull for any help!

EDIT: My PBNavigator File (that was requested):

//All modules that needs to be implemented
import {createAppContainer, createSwitchNavigator} from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import { Platform } from 'react-native';

//All imported screens from my screens folder
import ChatScreen from '../screens/ChatScreen';
import ContactsScreen from '../screens/ContactsScreen';
import MainScreen from '../screens/MainScreen';
import LoginScreen from '../screens/LoginScreen';
import PigeonBaseScreen from '../screens/PigeonBaseScreen';
import PigeonLexiconScreen from '../screens/PigeonLexiconScreen';
import ShopScreen from '../screens/ShopScreen';

//Importing other recources 
import Colors from '../constants/Colors';

//Setting up stack navigator
const PBNavigator = createStackNavigator({
    Main: {
        screen: MainScreen,
        navigationOptions:{
            headerShown: false,
        }
    },
    Chat: ChatScreen,
    Contacts: ContactsScreen,
    PigeonBase: PigeonBaseScreen,
    PigeonLexicon: PigeonLexiconScreen,
    Shop: ShopScreen
}, {
    defaultNavigationOptions:{
        backgroundColor: Platform.OS === 'android' ? Colors.primary : ''
    },
    headerTintColor: Platform.OS === 'android' ? 'white' : Colors.primary,
    
    }
);

const AuthNavigator = createStackNavigator({
    Auth:{
        screen: LoginScreen,
        navigationOptions:{
            headerShown: false,
        }
    } 
});

const MainNavigator = createSwitchNavigator({
    Auth: AuthNavigator, //LoginScreen you cant go back after you passed it
    Homescreen: PBNavigator //switching so PBNavigator when you finished authentification
});





export default createAppContainer(MainNavigator);

Upvotes: 1

Views: 3607

Answers (4)

Irfan wani
Irfan wani

Reputation: 5065

If anyone is still trying to figure out the problem in the given code, i think i got it;

const initialPigeonState = () => {
    myPigeons = []
};

This is the whole problem. Here initialPigeonState is defined as a function which does not return any thing. Thus causing error.

Now to get rid of the error, we can either define initialPigeonState as;

initialPigeonState = {
    myPigeons: []
} 

Or if you want to keep it as a function, then return an object from it;

const initialPigeonState = () => ({
    myPigeons: []
});

Parenthesis after the arrow notation means return and then what is inside it is returned (here an object).

Upvotes: 1

Michael Ceber
Michael Ceber

Reputation: 2452

Where do you declare MainScreen ? In PB navigator?

Upvotes: 1

ManSe
ManSe

Reputation: 81

state.myPigeons in main.js is undefined at the time of starting the application. If you are trying to perform some action on a redux object, the value should be defined in there. The best approach is to always have a defensive check in the code to check the object is not 'undefined'.

Upvotes: 1

Michael Ceber
Michael Ceber

Reputation: 2452

This code looks wrong

var generatedPigeon = Math.floor(Math.random() * ALLPIGEONS.length);
generatedPigeon.nickname = "Kuba";

Try more like

var generatedPigeonNo = Math.floor(Math.random() * ALLPIGEONS.length);
var generatedPigeon= ALLPIGEONS[generatedPigeonNo];
generatedPigeon.nickname = "Kuba";

Upvotes: 1

Related Questions