Reputation: 1024
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
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
}
}
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