Salman
Salman

Reputation: 1024

React Redux combineReducer | Reducer not being passed

My app has been using redux since the beginning, but only with one reducer. The cart reducer. Now I want to add another reducer and according to the redux docs, combineReducer is the way to go.

Now I've read trough the docs and looked trough the examples and tried implementing combineReducer to my apps store.

What I expected to happen:

Both reducers can be called individually whenever needed.

What happend:

Neither reducer works whenever using combineReducer, but each reducer works individual whenever but in the store.

Works:

import {createStore, combineReducers} from 'redux';
import cartItems from '../reducers/cartItems';
import getPayment from '../reducers/getPayment';

export default store = createStore(cartItems)

Doesn't work:

import {createStore, combineReducers} from 'redux';
import cartItems from '../reducers/cartItems';
import getPayment from '../reducers/getPayment';

const rootReducer = combineReducers({
    cartItems,
    getPayment,
  });

export default store = createStore(rootReducer)

Store -> Index.JS

import {createStore, combineReducers} from 'redux';
import cartItems from '../reducers/cartItems';
import getPayment from '../reducers/getPayment';

const rootReducer = combineReducers({
    cartItems,
    getPayment,
      });

export default store = createStore(rootReducer)

Reducer -> cartItems.JS

const cartItems = (state = [], action) => {
    switch (action.type)
    {
        case 'ADD_TO_CART':
        console.log('CarItems.JS', action.payload)
            if (state.some(cartItem => cartItem.id === action.payload.id)) {
                // increase qty if item already exists in cart
                return state.map(cartItem => (
                    cartItem.id === action.payload.id ? { ...cartItem, qty: cartItem.qty + 1 } : cartItem

                    ));

            }
            return [...state, { ...action.payload, qty: 1 }]; // else add the new item to cart

        case 'REMOVE_FROM_CART':
            return state
                .map(cartItem => (cartItem.id === action.payload.id ? { ...cartItem, qty: cartItem.qty - 1 } : cartItem))
                .filter(cartItem => cartItem.qty > 0);
    }
    return state
} 

export default cartItems 

App.JS

import React from 'react';
import AppNavigator from './navigation/AppNavigator';
import {Provider} from 'react-redux';
import store from './store';

export default class App extends React.Component {
    render() {
        return (
        <Provider store={store}>
            <AppNavigator />
        </Provider> 
        ) 
    }
}

CartScreen

import React, { Component } from 'react';
import { 
  View, 
  Text, 
  StyleSheet, 
  TouchableOpacity, 
  FlatList
} from 'react-native';
import {connect} from 'react-redux';
import Icon from "react-native-vector-icons/Ionicons";
import { addItemToCart, removeItem } from '../actions/ProductActionCreators';

const mapStateToProps = (state) => {
    let totalPrice = 0;
    state.map((item) => { // eslint-disable-line
      totalPrice += item.price * item.qty;
    });
    return {
        cartItems: state,
        totalPrice : totalPrice
    }
}
export class CartScreen extends Component{
    static navigationOptions = {
        header: null,
    };
    renderProducts = (products) => {
        return (
            <View key={products.index} style={styles.products}>
                <View style={styles.iconContainer}> 
                    <Icon name={products.item.icon} color="#DD016B" size={25} />
                </View>
                <View style={styles.text}>
                    <Text style={styles.name}>
                        {products.item.name} 
                    </Text>
                    <Text style={styles.price}>
                    € {products.item.price * products.item.qty}
                    </Text>
                </View>
                <View style={styles.buttonContainer}>
                    <TouchableOpacity style={styles.buttonContainer} onPress={() => this.props.removeItem(products.item)} > 
                        <Icon style={styles.button} name="ios-remove" color="white" size={25} />
                    </TouchableOpacity>
                    <Text style={styles.qty}>{products.item.qty}</Text>
                    <TouchableOpacity style={styles.buttonContainer} onPress={() => this.props.addItemToCart(products.item)} > 
                        <Icon style={styles.button} name="ios-add" color="white" size={25} />
                    </TouchableOpacity>

                </View>
            </View>
        )
    }
    render(){
        return (
            <View style={styles.container}>
                <Text style={styles.title}>Uw bestelling</Text>
                {console.log('CS', this.props.cartItems )}
                { 
                    this.props.cartItems.length>0?
                    <View>
                        <View style={styles.productContainer}>

                        <FlatList
                        style={styles.listContainer}
                        data={this.props.cartItems}
                        renderItem={this.renderProducts}
                        keyExtractor={(item, index) => index.toString()}
                        />
                        </View>
                        <View style={styles.optionsContainer}>
                            <Text style={styles.total}>Totaal: € {this.props.totalPrice} </Text>    
                            <TouchableOpacity style={styles.checkOutContainer}  onPress={() => this.props.navigation.navigate('Payment')}>  
                                <Icon style={styles.checkOutButton} name="ios-checkmark" color="white" size={35} />
                            </TouchableOpacity>
                        </View>
                    </View>
                    : 
                    <Text style={styles.emptyContainer}>No Items in your cart</Text>
                }
            </View>
        )
    }
}
const mapDispatchToProps =  {
    addItemToCart,
    removeItem
}
export default connect(mapStateToProps, mapDispatchToProps)(CartScreen);

CartIcon

import React from 'react';
import {
    View,
    Text,
    StyleSheet,
    Platform
} from "react-native";

import { withNavigation } from 'react-navigation';

import {connect} from 'react-redux';

import Icon from 'react-native-vector-icons/Ionicons';

const ShoppingCartIcon = (props) => (
    <View style={[{ padding: 5 }, Platform.OS == 'android' ? styles.iconContainer : null]}>
    <View style={{
        position: 'absolute', height: 30, width: 30, borderRadius: 15, backgroundColor: '#DD016B', right: 15, bottom: 15, alignItems: 'center', justifyContent: 'center', zIndex: 2000,

    }}>
        <Text style={{ color: 'white', fontWeight: 'bold' }}>{props.cartItems.length}</Text>
    </View>
    <Icon onPress={() => props.navigation.navigate('Cart')} name="ios-cart" color="white" size={30} />
</View>
) 

const mapStateToProps = (state) => {
    const {cartItems, getPayment} = state;
    return {
        cartItems: state
    }
}

 export default connect(mapStateToProps)(withNavigation(ShoppingCartIcon));

 const styles = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center'
    },
    iconContainer: {
        paddingLeft: 20, paddingTop: 10, marginRight: 5
    }
});

Upvotes: 0

Views: 1684

Answers (1)

wicky
wicky

Reputation: 978

When you use combineReducer, you need to access your redux state in a different way. Your cartItems and getPayment state can be accessed in your mapStateToProps by using state.cartItems and state.getPayment respectively. Here is a way you can update your mapStateToProps function.

const mapStateToProps = (state) => {
    const {cartItems, getPayment} = state;
    let totalPrice = 0;
    cartItems.map((item) => { // eslint-disable-line
      totalPrice += item.price * item.qty;
    });
    return {
        cartItems: cartItems,
        totalPrice : totalPrice
    }
}


Update

For CartIcon, you can use it this way

const mapStateToProps = (state) => {
    return {
        cartItems: state.cartItems
    }
}

To explain, lets say if this is your current cartItems state, which you exported from Reducer -> cartItems.JS

{
  itemOne: {
    price: '$10',
    qty: 20
  },
  itemOne: {
    price: '$10',
    qty: 20
  },
}

When you use combineReducers, the state you get in your mapStateToProps function will be like this. You can do a console.log(state) to check

const mapStateToProps = (state) => {
  console.log(state);
  /** this console.log will prints something like this
      state: {
        cartItems: {
          itemOne: {
            price: '$10',
            qty: 20
          },
          itemOne: {
            price: '$10',
            qty: 20
          },
        }
      },
      getPayment: {
        //your getPayment states
      }
  **/
  return {
    cartItems: state.cartItems
  }
}

Thats why to access your cart items, you need to do state.cartItems

Upvotes: 1

Related Questions