Redux not working on React Native (Invariant Violation) - reactjs

Error Message:
https://imgur.com/LBNyl2M
App.js
import React, { Component } from 'react';
import { StyleSheet } from 'react-native';
import { Provider } from 'react-redux';
import Layout from './containers/Layout/Layout';
import store from './store/index';
export default function App() {
return (
<Provider store={store}><Layout/></Provider>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
index.js
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer);
export default store;
reducer.js
import * as actionTypes from './actions';
import moment from 'moment';
const initialState = {
itemList: [],
idCount: 0,
text: 'Write your to do item!',
chosenDate: 'no-date',
activeItems: 0,
completedItems: 0,
showCompletedList: false
}
const reducer = (state = initialState, action) => {
switch(action.type){
case actionTypes.ADDTOLIST:
const objItem = { 'id': state.idCount+1, 'text': state.text, 'date': state.chosenDate, 'completed': false, 'displayTextInput': false, 'pickerVisible': false };
return {
...state,
itemList: [...state.itemList, objItem],
idCount: state.idCount+1,
activeItems: state.activeItems+1
}
case actionTypes.REMOVEFROMLIST:
let oldItemList = [...state.itemList];
let index = oldItemList.indexOf(action.item);
if( index !== -1) oldItemList.splice(index, 1);
return {
...state,
itemList: [...oldItemList],
activeItems: action.item.completed ? state.activeItems : state.activeItems-1,
completedItems: action.item.completed ? state.completedItems-1 : state.completedItems
}
case actionTypes.EDITITEMDATE:
oldItemList = [...state.itemList];
index = oldItemList.indexOf(action.item);
if(index !== -1){
oldItemList[index].date = state.chosenDate;
return {
...state,
itemList: [...oldItemList]
}
}
return state;
case actionTypes.EDITITEMSTATUS:
oldItemList = [...state.itemList];
index = oldItemList.indexOf(action.item);
if(index !== -1){
oldItemList[index].completed = !oldItemList[index].completed;
return {
...state,
itemList: [...oldItemList],
activeItems: action.item.completed ? state.activeItems+1 : state.activeItems-1,
completedItems: action.item.completed ? state.completedItems-1 : state.completedItems+1
}
}
return state;
case actionTypes.EDITITEMTEXT:
oldItemList = [...state.itemList];
index = oldItemList.indexOf(action.item);
if(index !== -1){
oldItemList[index] = state.text;
return {
...state,
itemList: [...oldItemList]
}
}
return state;
case actionTypes.TOGGLETEXTINPUT:
oldItemList = [...oldItemList];
index = oldItemList[index].indexOf(action.item);
if(index !== -1){
oldItemList[index],displayTextInput = !oldItemList[index],displayTextInput;
return {
...state,
itemList: [...oldItemList]
}
}
return state;
case actionTypes.FILTERACTIVEITEMS:
return {
...state,
showCompletedList: false
}
case actionTypes.FILTERCOMPLETEDITEMS:
return {
...state,
showCompletedList: true
}
case actionTypes.HANDLECHANGETEXT:
return {
...state,
text: action.text
}
case actionTypes.HIDEPICKERINITEM:
oldItemList = [...state.itemList];
index = oldItemList[index].indexOf(item);
if(index !== -1){
oldItemList[index].isVisible = false;
return {
...state,
itemList: [...oldItemList]
}
}
case actionTypes.SHOWPICKERINITEM:
oldItemList = [...state.itemList];
index = oldItemList[index].indexOf(item);
if(index !== -1){
oldItemList[index].isVisible = true;
return {
...state,
itemList: [...oldItemList]
}
}
return state;
case actionTypes.HANDLEPICKER:
return{
...state,
chosenDate: moment(action.datetime).format('MMM, Do YYYY HH:mm')
}
}
}
export default reducer;
Layout.js
import React, { Component } from 'react';
import { Button, KeyboardAvoidingView } from 'react-native';
import AddScreen from '../../components/AddScreen/AddScreen';
import TodoList from '../../components/TodoList/TodoList';
import Header from '../../components/UI/Header/Header';
class Layout extends Component {
state = {
displayItems: false,
pickerVisible: false
}
toggleItems = () => {
this.setState({ displayItems: !this.state.displayItems });
}
showPicker = () => {
this.setState({ pickerVisible: true });
}
hidePicker = () => {
this.setState({ pickerVisible: false });
}
render () {
let childComponent = <AddScreen
toggleItems={this.props.toggleItems}
showPicker={this.props.showPicker}
hidePicker={this.props.hidePicker}
pickerVisible={this.props.pickerVisible}
/>;
if(this.props.displayItems){
childComponent = <TodoList
itemList={this.state.itemList}
showCompletedList={this.state.showCompletedList}
/>;
}
return (
<KeyboardAvoidingView style={{flex:1}} behavior="padding">
<Header />
{childComponent}
<Button title='Toggle Items' onPress={this.toggleItems} />
</KeyboardAvoidingView>
);
}
}
export default Layout;
my complete project on github: https://github.com/rvmelo/todolist-redux
Is it an error in the code or is it related to some package update?
I get the following error: "Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports."

You're going to have to do some tracing down on your end. That error typically comes when you try to render a component that doesn't exist (or doesn't exist at the path you've told it it does), which is impossible to tell without having access to most of the project.
In AddScreen, TodoList, and Header make sure they all export default. And if they render any outside components you will need to check the same for them if imported the same way.
One way to debug this would be to remove each component one at a time and see which one makes it break.
-------UPDATE---------
Looking at your project on GitHub, I see that two of the components are named exports.
Change Layout to the following:
import { AddScreen } from '../../components/AddScreen/AddScreen';
import { TodoList } from '../../components/TodoList/TodoList';
Explanation
When you do export myFunction, its a named export. So you must import it with the named import syntax like this:
import { myFunction } from '..'
When you do export default myFunction, its a default export and can be imported as anything, so you don't use the curly braces and instead do this:
import Anything from '...' //path to myFunction

Related

Adding events to ReactBigCalendar using redux

I'm currently working on a booking app, and in that regard I need to fetch some data from an API. I'm using react + redux to do this, but I can't seem to get this element BigCalendar to update when the state is updated. BigCalendar has to have some kind of object for it's init process.
BookingReducer.js
import { fetchBookingStart, fetchBookingsSuccess, fetchBookingsError } from '../types.js';
const initialState = {
fetching: false,
fetched: false,
events: [],
error: null
}
export default function (state = initialState, action) {
switch (action.type) {
case fetchBookingStart:
return {
...state,
fetching: true,
fetched: false
}
case fetchBookingsSuccess:
return {
...state,
fetching: false,
fetched: true,
events: action.payload.bookings.length < 0 ? [] : action.payload.bookings.map(B => {
return {
id:22,
title: "testTitle",
description: B.description,
start: B.dateFrom,
end: B.dateTo,
room: B.room,
user: B.user
}
})
}
case fetchBookingsError:
return {
...state,
fetching: false,
error: action.payload.error
}
default:
return state;
}
}
bookingAction.js
import axios from "axios";
import { fetchBookingStart, fetchBookingsSuccess, fetchBookingsError } from '../types.js';
const apiUrl = "http://localhost/api/booking"; //CHANGE FOR PROD!
export const getBookings = () => dispatch => {
let allUrl = apiUrl + "/find/";
dispatch({type: fetchBookingStart});
axios.get(allUrl).then(response => {
console.log(response.data);
dispatch({
type: fetchBookingsSuccess,
payload: response.data
})
}).catch(error => {
dispatch({
type: fetchBookingsError,
payload: error
});
});
}
App.js
import React, { Component } from "react";
import { connect } from 'react-redux';
import { getBookings } from './components/redux/actions/bookingActions';
import Link from "react-router-dom/Link";
import BigCalendar from 'react-big-calendar';
import moment from 'moment';
import 'react-big-calendar/lib/css/react-big-calendar.css';
import "./style.css";
import 'moment/locale/da';
import BookingDialog from './components/booking_create_dialog/BookingDialog';
import Dialog from "material-ui/Dialog/Dialog";
import FlatButton from "material-ui/FlatButton/FlatButton";
let isDialogOpen = false;
moment.locale('da');
BigCalendar.momentLocalizer(moment);
function eventStyleGetter(event, start, end, isSelected) {
let style = { backgroundColor: "" }
switch (event.room) {
case "sal":
style.borderColor = "#781B7F";
style.backgroundColor = "#781B7F";
break;
case "cafe":
style.borderColor = "#067F3D";
style.backgroundColor = "#067F3D";
break;
default:
break;
}
return { style: style };
}
class App extends Component {
componentWillMount() {
this.props.getBookings();
}
componentDidUpdate() {
if (this.props.fetched === true && this.props.fetching === false) {
this.refs.BigCalendar.forceUpdate();
}
}
render() {
return (
<div className="MainContainer">
{/*<Link to="/login"><FlatButton>Login</FlatButton></Link>*/}
<div className="CalendarContainer">
<BigCalendar
ref="BigCalendar"
selectable
className="Calendar"
events={this.props.events}
defaultView="week"
defaultDate={new Date()}
step={60}
eventPropGetter={eventStyleGetter}
/>
</div>
<div className="TestContainer">
<button onClick={() => {
this.props.events.push({
id: 55,
title: "test event",
allDay: true,
start: new Date(),
end: new Date()
}); console.log(this.props.events)
}}> bookigns </button>
{this.props.events.map(E => <h1> {E.room} </h1>)}
</div>
</div>
)
}
}
const mapStateToProps = state => ({
events: state.bookings.events,
fetched: state.bookings.fetched,
fetching: state.bookings.fetching
})
export default connect(mapStateToProps, { getBookings })(App);
Please point out any mistakes that can help me along the right way of doing this.
I was having a similar problem with my react-redux setup and realized my events array of objects wasn't formatted correctly. Other than checking this, I'd also check to see if BigCalendar is getting rendered with anything from this.props.events in the first place. Every time the events prop is changed for BigCalendar it updates itself.

State doesnt get reset on route change - React-redux-router

Hi I am creating a Server side react app. I have multiple routes using the same components and reducers. One specific reducer is an ItemsPerPage dropdown. I am getting the value from the reducer and passing it as a payload to a post request to a database to fetch that many results.
In one page, I get 50 results, and when I navigate to the other page using the same reducer, the state value should be 10 but rather its 50. How do I reset the state when going to another page?
I am using { LOCATION_CHANGE } from 'react-router-redux'
routerReducer.js:
import { LOCATION_CHANGE } from 'react-router-redux';
const initialState = {
location: '',
};
export const routeReducer = (state = initialState, action) => {
switch (action.type) {
case LOCATION_CHANGE:
return {
...state,
...action.payload,
};
default:
return state;
}
};
ItemsPerPageDropdown:
import React, {PropTypes, Component} from 'react';
import _ from 'lodash';
import { connect } from 'react-redux';
import { changeItemsPerPage } from '../../actions/index';
class ItemsPerPage extends Component {
handleChange = (event) => {
this.props.changeItemsPerPage(event.target.value)
};
render() {
const itemsPerPage = [10, 20, 50];
return (
<div className={'table-item-count-container'}>
<label className={'filter-label items-by-page-label'}>Items Per Page:</label>
<select id="items-per-paghe"
className="form-control items-by-page-select"
onChange={this.handleChange}
>
{_.map(itemsPerPage, (item, index) => <option key={index}>{item}</option>)}
</select>
</div>
)
}
}
export default connect(null, {changeItemsPerPage})(ItemsPerPage);
ItemsPerPageReducer:
import * as ACTION_TYPES from '../consts/action_types';
const initialState = {
items: 10,
};
export const itemsPerPageReducer = (state = initialState, action) => {
switch (action.type) {
case ACTION_TYPES.CHANGE_ITEMS_PER_PAGE:
return {
...state,
items: action.data,
};
default:
return state;
}
};
Main page using this component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom'
import { withRouter } from 'react-router'
import { bindActionCreators } from 'redux';
import _ from 'lodash';
import Moment from 'react-moment';
import Header from '../../components/Header/Header';
import DataTable from '../../components/DataTable/DataTable';
import InstructionList from '../../components/InstructionList/InstructionList';
import { getData, postData } from '../../actions';
import { columns } from './Report1Columns';
import * as instructions from '../../consts/instructionText';
class Report1 extends Component {
params = {
userId: this.props.corpId,
filteredAppID: '',
envClassification: '',
Status: '',
startRow: 0 + this.props.activePage, //1
numRows: this.props.itemsPerPage,
sortCol: 'application_name',
sortDir: 'asc',
};
loadPage = () => {
if(this.props.postData) {
this.props.postData('https://localhost:3001/reports/report1/', this.params);
}
};
componentDidMount = () => {
this.props.postData('https://localhost:3001/reports/report1/', this.params);
};
componentWillReceiveProps = (nextProps) => {
if (nextProps.itemsPerPage !== this.props.itemsPerPage) {
this.params.numRows = nextProps.itemsPerPage;
this.loadPage();
}
if(nextProps.activePage !== this.props.activePage) {
this.params.startRow = ((nextProps.activePage - 1) * this.props.itemsPerPage) +1;
this.loadPage();
}
if(nextProps.searchTerm !== this.props.searchTerm) {
this.params.filteredAppID = nextProps.searchTerm;
this.loadPage();
}
if(nextProps.envClassification !== this.props.envClassification) {
this.params.envClassification = nextProps.envClassification === 'All' ? '' : nextProps.envClassification;
this.loadPage();
}
if(nextProps.watchtowerStatus !== this.props.Status) {
this.params.watchtowerStatus= nextProps.watchtowerStatus=== 'Manage & Analyze' ? '' : nextProps.watchtowerStatus;
this.loadPage();
}
};
render() {
return (
<div>
<Header title={ 'Report 1' } />
<InstructionList instructions={ instructions.Report1 } />
{this.props.data &&
<DataTable
keyField={ 'Report1' }
columns={ columns }
paginatedData={ this.props.data }
totalRows={ this.props.totalRows }
placeholder={ 'ID/NAME' }
showStatus={true}
/>}
</div>
);
}
}
const mapStateToProps = state => ({
/**
* The date to be passed to the table
*/
data: _.get(state.isFetchingPost, 'data.rows'),
/**
* Total Rows count of data
*/
totalRows: state.isFetchingPost.data.total_rows,
/**
* Items Per Page
*/
itemsPerPage: state.itemsPerPageReducer.items,
/**
* Item which is searched
*/
searchTerm: state.searchReducer.searchTerm,
/**
* The current active page for pagination
*/
activePage: state.changeActivePageReducer.activePage,
/**
* The value of the dropdown selected in Environment Classification
*/
envClassification: state.envClassificationReducer.envClassification,
/**
* The value of the dropdown selected in Status
*/
watchtowerStatus: state.watchTowerStatusReducer.watchTowerStatus
});
const mapDispatchToProps = dispatch => bindActionCreators({ getData, postData }, dispatch);
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Report1));
If you can see in the images below, I navigate between the routes and state still remains same instead of taking initial state.
Add a case for LOCATION_CHANGE to your ItemsPerPageReducer to reset the count when the location changes:
import * as ACTION_TYPES from '../consts/action_types';
import { LOCATION_CHANGE } from 'react-router-redux';
const initialState = {
items: 10,
};
export const itemsPerPageReducer = (state = initialState, action) => {
switch (action.type) {
case ACTION_TYPES.CHANGE_ITEMS_PER_PAGE:
return {
...state,
items: action.data,
};
case LOCATION_CHANGE:
return {
...state,
items: 10,
};
default:
return state;
}
}
};
If you only want it to reset on certain location changes, you can check action.payload to test if it is a route you actually want to reset on.

How to "bubble up" errors from thrown from redux connect functions?

I want my react native app to display error messages with a toast. I want to detect errors within the root level App component using componentDidCatch so I can handle all errors in the same manner.
Currently, if one of my async actions throw an error, mapDispatchToProps can catch it. How do I "bubble" up these errors to my App component?
Alternatively, I could add a redux state for errors and set it on every async error. I can then check for this state in App. It would be cleaner, however, if I could catch all errors in componentDidCatch
Well, here's what I did in my project. I use https://github.com/fkhadra/react-toastify
App.js
import Toaster from './components/Toaster/Toaster';
class App extends Component {
render() {
return (
<div>
<Toaster/>
<Routes />
</div>
);
}
}
export default (App);
Toaster.js
import React, { Component } from 'react';
import { connect } from "react-redux";
import { toast, ToastContainer } from 'react-toastify';
import PropTypes from 'prop-types';
import { toastConstants } from '../../_constants';
const Message = ({ type, content }) => {
let icon = '';
switch(type){
case 'success':
icon = <i className="fa fa-check-circle"></i>;
break;
case 'error':
icon = <i className="fa fa-times-circle"></i>;
break;
case 'info':
icon = <i className="fa fa-info-circle"></i>;
break;
case 'warning':
icon = <i className="fa fa-exclamation-circle"></i>;
break;
default:
icon = '';
break;
}
return (
<div>
{icon} {content}
</div>
);
};
class Toaster extends Component {
componentWillReceiveProps(nextProps) {
if (nextProps.toast.message && nextProps.toast.type) {
toast.dismiss();
switch (nextProps.toast.type) {
case toastConstants.SUCCESS:
toast.success(<Message content={nextProps.toast.message} type="success" />);
break;
case toastConstants.INFO:
toast.info(<Message content={nextProps.toast.message} type="info" />);
break;
case toastConstants.WARN:
toast.warn(<Message content={nextProps.toast.message} type="warning" />);
break;
case toastConstants.ERROR:
toast.error(<Message content={nextProps.toast.message} type="error" />);
break;
default:
break;
}
}
}
render() {
return (
<ToastContainer autoClose={5000} />
);
}
}
function mapStateToProps(state) {
const { toast } = state;
return {
toast
};
}
Message.propTypes = {
type: PropTypes.string,
content: PropTypes.string
};
export default connect(mapStateToProps)(Toaster);
SomeActions.js
function getAll(){
return dispatch => {
dispatch(request());
companyService.getAll()
.then(
response => {
if(response.status === 'fail'){
dispatch(failure(response));
dispatch(toastActions.error(response.message));
}else{
dispatch(success(response));
}
},
error => {
dispatch(toastActions.error(error.toString()));
dispatch(failure(error.toString()));
}
);
}
function request() { return { type: companyConstants.LIST_REQUEST } }
function success(data) { return { type: companyConstants.LIST_SUCCESS, data } }
function failure(error) { return { type: companyConstants.LIST_FAILURE, error } }
}
toastActions.js
import { toastConstants } from '../_constants';
export const toastActions = {
success,
error,
clear
};
function success(message) {
return { type: toastConstants.SUCCESS, message };
}
function error(message) {
return { type: toastConstants.ERROR, message };
}
function clear() {
return { type: toastConstants.CLEAR };
}
toastReducer.js
import { toastConstants } from '../_constants';
const initialState = {
type: toastConstants.CLEAR,
message: null
};
export function toast(state = initialState, action) {
switch (action.type) {
case toastConstants.SUCCESS:
return {
type: toastConstants.SUCCESS,
message: action.message
};
case toastConstants.ERROR:
return {
type: toastConstants.ERROR,
message: action.message
};
case toastConstants.CLEAR:
return {};
default:
return initialState
}
}
Hope its of any use for you!
Cheers.
so the issue is not specific to redux connector functions. in fact, all errors thrown from event handlers will not trigger componentDidCatch. see https://reactjs.org/docs/error-boundaries.html#how-about-event-handlers.
I did not want use some redux error state to capture these errors as that would require more boilerplate. ex: it would force me to connect all my components to redux for error handling, even for components that did not need redux state or would not update state (except for error state). not the idea solution, but to get around this I created a separate function for all my event handlers to use.
//componentEventHandler.js
export function handleEvent () {
const args = Array.from(arguments);
const fn = args.shift();
fn(...args).catch(e => this.setState(() => { throw e }));
}
I then import this function in my components and use as follows.
onPress={handleEvent.bind(this, this.props.signIn, this.state.email, this.state.password)}
now all my child components of App.js will event errors up to App.js

Change in state do not propagate in props

I have the "classic" issue with the React redux about not propagating the change in state into the props when I try to access it in the component.
Here I have read that
99.9% of the time, this is because you are accidentally mutating data, usually in your reducer
Can you tell me what am I doing wrong? Is this the good way how to do the deep copy of the property of the specified object in array?
note: in the reducer in the return statement the state is clearly changed correctly (debugged it)
reducer:
case 'TOGGLE_SELECTED_TAG':
const toggledTagId = action.payload;
const index = findItemById(state.tags, toggledTagId);
const newTags = state.tags.slice(0);
if(index >= 0)
{
newTags[index] = Object.assign(
state.tags[index],
{selected: !state.tags[index].selected});
state.tags = newTags;
}
return Object.assign({}, state);
component:
import React from 'react';
import { Button, FormControl, Table, Modal } from 'react-bootstrap';
import { connect } from 'react-redux';
import axios from 'axios';
import {selectTagAction} from '../../actions/actions'
#connect((store) => {
return {
tags: store.TagsReducer.tags,
}
})
export default class AssignTag extends React.Component {
constructor(props) {
super(props);
this.handleTagClick = this.handleTagClick.bind(this);
}
handleTagClick(element) {
debugger;
this.props.dispatch(selectTagAction(element));
}
render() {
const tags = this.props.tags;
console.log(tags);
const mappedTags = tags.map(tag => {
return (
<div className="col-sm-12" key={tag.id} onClick={() => this.handleTagClick(tag.id)}
style={{background: this.getBackgroundColor(tag.selected)}}>
<span>{tag.name}</span>
</div>
)
})
// code continues
}
}
You are indeed mutating the state. Try this:
case 'TOGGLE_SELECTED_TAG':
const toggledTagId = action.payload;
const index = findItemById(state.tags, toggledTagId);
let newTags = state;
if( index >= 0 )
{
newTags[index] = Object.assign(
{},
state.tags[index],
{ selected: !state.tags[index].selected }
);
//state.tags = newTags; This line essentially mutates the state
return Object.assign( {}, state, { tags: newTags });
}
return state;
Another workaround to avoiding mutation of state is to use the ES6 shorthand in your reducer:
.... return { ...state, tags : newTags };

Update redux state in two places with one action

I am building a webshop and I´ve run in to some problems. When I click on the buy button I want three things to happen:
Add the product to the redux state (shoppingcartReducer)
Update the sum and quantity in the redux state (shoppingcartReducer)
Render out the sum and quantity in my shoppingCart.js
component
The problem is in the shoppingcartReducer. I dont know how to update the state in two different paths on one action.
return state
.setIn(['products', action.articleNr], addArticle(action, state))
Here I am in the 'products/articleNr' path and manipulating data, but I also wants to update the sum and quantity in './'.
So my alternatives that I thought of are maybe to have some sort of middleware thing in the shoppingcartActions but I don´t really know if this is right, nor how to do it!
I appreciate all the help, thanks!
shoppingcartReducer:
import Immutable from 'seamless-immutable'
import { addToShoppingcart, createShoppingCart } from '../actions/shoppingcartActions'
const ADD_TO_SHOPPINGCART = 'ADD_TO_SHOPPINGCART'
const CREATE_SHOPPING_CART = 'CREATE_SHOPPING_CART'
const initialState = Immutable({
sum: 0,
quantity: 0
})
export default function shoppingcartReducer(state = initialState, action) {
switch (action.type) {
case ADD_TO_SHOPPINGCART:
return state
.setIn(['products', action.articleNr], addArticle(action, state))
case CREATE_SHOPPING_CART:
return state
.set(action.id, createCart(action))
}
return state
}
function addArticle(action, state) {
return {
product: action.product
}
}
function createCart(action) {
return {
id: action.id,
}
}
shoppingcartActions:
let nextTodoId = 0
const ADD_TO_SHOPPINGCART = 'ADD_TO_SHOPPINGCART'
const CREATE_SHOPPING_CART = 'CREATE_SHOPPING_CART'
export function addToShoppingcart(product) {
return {
type: ADD_TO_SHOPPINGCART,
articleNr: product.articleNr,
product: product,
}
}
export function createShoppingCart() {
return {
type: CREATE_SHOPPING_CART,
id: 'productNr:'+nextTodoId++,
}
}
ShoppingCart.js:
import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as shoppingcartActions from '../../actions/shoppingcartActions'
class ShoppingCart extends Component {
componentWillMount() {
this.state = {
shoppingcartReducer: []
}
}
componentWillReceiveProps(nextProps) {
this.setState({
shoppingcartReducer: nextProps.shoppingcartReducer ? nextProps.shoppingcartReducer : ''
})
}
render() {
const { shoppingcartReducer } = this.props
const { sum, quantity } = shoppingcartReducer
return (
<div className="shoppingCart">
<ul>
<li>Summa: {sum} :-</li>
<li>Antal varor: {quantity}</li>
</ul>
</div>
)
}
}
function mapStateToProps(state) {
return {
shoppingcartReducer: state.shoppingcartReducer
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(shoppingcartActions, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(ShoppingCart)
This worked!
export function addProduct(product) {
return {
type: ADD_PRODUCT,
articleNr: product.articleNr,
product: product,
}
}
export function addSummary(product) {
return {
type: ADD_SUMMARY,
product: product
}
}
export function addToShoppingcart(product) {
return (dispatch) => {
dispatch( addProduct(product))
dispatch( addSummary(product))
}
}

Resources