0

I am making an application with React-native and Redux. The problem is that redux doesn't seem to be returning a valid state when running the program. My classes are as follows:

configure.js

import { createStore, combineReducers, updateMiddleware, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import { dishes } from './dishes';
import { promotions } from './promotions';
import { comments } from './comments';
import { leaders } from './leaders';

export const ConfigureStore = () => {
    const store = createStore(
        combineReducers({
            dishes,
            comments,
            promotions,
            leaders
        }),
        applyMiddleware(thunk, logger)
    );

    return store;
}

baseUrl.js

export const baseUrl = 'http://localhost:3001/';

I have a similar file for each object: ** dihses.js**

import * as ActionTypes from './ActionTypes';

export const dishes = (state ={
    isLoading: true, // it is true if the data is being loaded from the sever
    errMess: null, // Message to be shown if there is any error
    dishes: [] // array that contains the details of the dishes
}, action ) => {
    switch(action.type){
        case ActionTypes.ADD_DISHES:
            return {...state, isLoading: false, errMess: null, dishes: action.payload};

        case ActionTypes.DISHES_LOADING:
            return {...state, isLoading: true, errMess: null, dishes: []}

        case ActionTypes.DISHES_FAILED:
            return {...state, isLoading: false, errMess: action.payload};

        default:
          return state;
    }
};

App.js

import React from 'react';
import Main from './components/MainComponents';
import { Provider } from 'react-redux';
import { ConfigureStore } from './redux/configure';

const store = ConfigureStore();

// It just renders the mainComponent
export default class App extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <Main />
      </Provider>
    );
  }
}

MainComponent.js

import React, { Component } from 'react';
import {Image, StyleSheet, ScrollView, View, Text } from 'react-native';
import { NavigationContainer, DrawerActions } from '@react-navigation/native';
import { createDrawerNavigator, DrawerItemList, } from '@react-navigation/drawer';
import { SafeAreaView } from 'react-native-safe-area-context';
import MenuNavigator from './navigation/MenuNavigator';
import ContactNavigator from './navigation/ContactNavigator';
import AboutUsNavigator from './navigation/AboutUsNavigator';
import HomeNavigator from './navigation/HomeNavigator';
import { Icon } from 'react-native-elements';
import "@fortawesome/fontawesome-free/js/all";
import { connect } from 'react-redux';
//import { baseUrl } from '../shared/baseUrl';
import { fetchDishes, fetchComments, fetchPromos, fetchLeaders } from '../redux/ActionCreators';

const mapStateToProps = state => {
    return {
    }
  }
  
const mapDispatchToProps = dispatch => ({
  fetchDishes: () => dispatch(fetchDishes()),
  fetchComments: () => dispatch(fetchComments()),
  fetchPromos: () => dispatch(fetchPromos()),
  fetchLeaders: () => dispatch(fetchLeaders()),
})

const MainNavigator = createDrawerNavigator()
const DrawerNavigation  = () => (
  <MainNavigator.Navigator backgroundColor='#D1C4E9' drawerContent={(props) => <CustomDrawerContentComponent {...props} />} >
    <MainNavigator.Screen name="Home" component={HomeNavigator} options={{drawerIcon:({ color }) => (<Icon name="home" type="font-awesome" size={24} color={color}/>)}}/>
    <MainNavigator.Screen name="Menu" component={MenuNavigator} options={{drawerIcon:({ color }) => (<Icon name="th-list" type="font-awesome" size={24} color={color}/>)}}/>
    <MainNavigator.Screen name="Contact" component={ContactNavigator} options={{drawerIcon:({ color }) => (<Icon name="address-card" type="font-awesome" size={22} color={color}/>)}}/>
    <MainNavigator.Screen name="About us" component={AboutUsNavigator} options={{drawerIcon:({ color }) => (<Icon name='info-circle' type="font-awesome" size={24} color={color}/>)}}/>
  </MainNavigator.Navigator>
)

const CustomDrawerContentComponent = (props) => (
  <ScrollView>
    <SafeAreaView style={styles.container} forceInset={{ top: 'always', horizontal: 'never' }}>
      <View style={styles.drawerHeader}>
        <View style={{flex:1}}>
        <Image source={require('./images/logo.png')} style={styles.drawerImage} />
        </View>
        <View style={{flex: 2}}>
          <Text style={styles.drawerHeaderText}>Ristorante Con Fusion</Text>
        </View>
      </View>
      <DrawerItemList {...props} />
    </SafeAreaView>
  </ScrollView>
);

class Main extends Component {
  // When the main component mounts, the program will issue the dispatch for the four of them. Each of them will issue a fetch to the server to obtain the data
  // once the data is obtained, it will be loaded into the Redux store
  componentDidMount() {
    this.props.fetchDishes();
    this.props.fetchComments();
    this.props.fetchPromos();
    this.props.fetchLeaders();
  }

  render() {
 // as two elements can not be returned at the same time, it is necessary to embed them inside a View
 // Uses menu dishes passing it the state of the dishes
 // We add this Platform.OS == "ios"? 0 : Expo.Constants.statusBarHeight because, in android, with stack navigation the app will add a bar with back arrow at the paddingTop
      return (
        <NavigationContainer>
            <DrawerNavigation/>
        </NavigationContainer>    
      );
  }
 
}
const styles = StyleSheet.create({
  container:{
    flex:1
  },
  drawerHeader:{
    backgroundColor: '#512DA8',
    height: 140,
    alignItems: 'center',
    justifyContent: 'center',
    flex: 1,
    flexDirection: 'row'
  },
  drawerHeaderText:{
    color: 'white',
    fontSize: 24,
    fontWeight: 'bold'
  },
  drawerImage:{
    margin: 10,
    width: 80,
    height: 60
  }
})

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

HomeComponent.js

import { connect } from 'react-redux';
import { baseUrl } from '../shared/baseUrl';

const mapStateToProps = state => {

    return {
      dishes: state.dishes
    }
  }

function RenderItem(props)
{
    const item = props.item;

    if (item!=null)
    {
        return(
            <Card
            featuredTitle = {item.name}
            featuredSubtitle = {item.designation}
            image={ {uri: baseUrl + item.image} }
            >
                <Text style={{margin:10}}>{item.description}</Text>
            </Card>
            
        );
    }
    else
    {
        return(<View><Text>NULL ITEM</Text></View>
            
            );
    }
}
class Home extends Component{
    
    static navigationOptions ={
        title: 'Home',
        drawerLabel: 'Home',
        backgroundColor: '#d1d9e3'
    };

    render(){
        return(
            <ScrollView>
                <Text>this.props.dishes.dishes: {this.props.dishes.dishes}</Text>
                <RenderItem
                // Only select items whose featured prop is set to true
                    item = {this.props.dishes.dishes.filter((dish) => dish.featured)[0]}
                
            </ScrollView>
        );
    }
}

export default connect(mapStateToProps)(Home);

My problem is that, the renderItem part, doesn't seem to be receiving the dishes object, but rather it executes the for part of "null", so I would like to know what I have to change in order to make a successful communication between the store and my app.

pepito
  • 433
  • 1
  • 3
  • 15

1 Answers1

1

When you are creating your store you should pass the initial state as the second value.

export const ConfigureStore = () => {
  const store = createStore(
    combineReducers({
        dishes,
        comments,
        promotions,
        leaders
    }),
    INITIALSTOREOBJECT,
    applyMiddleware(thunk, logger)
  );

  return store;
}

You can read more about the createStore function in Redux in the official docs here.

p-syche
  • 575
  • 1
  • 5
  • 17
  • Thank you for your answer! I did that by adding an empty initial state ( {dishes:[], comments: [], promotions:[], leaders:[]}) but did not work, as it gives me the error "Cannot read property 'filter' of undefined" at HomeComponent.js. Do you have any other idea? – pepito Jul 26 '20 at 16:40
  • 1
    I noticed in the code snippet you posted you call the object in the HomeComponent with 'this.props.dishes.dishes'. I think you may have too many dishes there :) – p-syche Jul 26 '20 at 21:11
  • 1
    When deleting the extra "dishes" I get TypeError: this.props.dishes.filter is not a function, which makes me think that the "dishes" part was needed (I put it there because I have the dishes.js file where I have a "dishes" array , which I have not added above, I will edit my question so that you can see it). Thank you for your help! – pepito Jul 27 '20 at 06:44
  • You may want to make sure that the `dishes` is in fact an Array. Take a look at this question/answers about the .filter function: https://stackoverflow.com/questions/55458675/filter-is-not-a-function/55458751 – p-syche Jul 27 '20 at 08:18
  • I get the data from a JSON with the form{ "dishes": [ { "id": 0, "name": "pizzaName", "image": "images/pizzaq.png", "category": "mains", "label": "Hot", "price": "4.99", "featured": true, "description": "..." },{...}]} . When printing "typeof(this.props.dishes.dishes)" it seems to be an "object", but whenever I try to execute a function (such as values or entries, to convert it into a pure array) I get the error " Cannot convert undefined or null to object" – pepito Jul 27 '20 at 09:04
  • So it seems something is wrong with the dishes object initialization. Try removing the filter function and checking if the object is present in the component and what it looks like. – p-syche Jul 27 '20 at 10:09