I am still new to react and redux and have been working on a project using react-router v2 and redux v3, but want to make the migration to react-router v4, as I think I understand the routing of nested components better and will find the workflow a lot easier. So I have gone through all the guides on migration and I am getting
Uncaught TypeError: Cannot read property 'detail' of undefined
from the following
function mapStateToProps(state) {
return {
whisky: state.whiskies.detail,
distillery: state.distillery.detail,
auth: state.auth,
profile: _.get(state.profile, 'profile', null)
};
}
I have tried various different solutions here on S.O. but none have resolved this. Here are my reducers, sagas etc, I am obviously missing something, but still being fairly new I just cannot see it.
app.js
import React from 'react';
import ReactDOM from 'react-dom';
import Routes from './routes/Routes.jsx';
import {BrowserRouter, Router, Route, Switch} from 'react-router-dom';
import {createStore, combineReducers, applyMiddleware} from 'redux';
import thunkMiddleware from 'redux-thunk';
import createSagaMiddleware from 'redux-saga';
import reducers from './redux/reducers';
import sagas from './redux/sagas';
import {createLogger} from 'redux-logger';
import { ConnectedRouter, routerReducer, routerMiddleware, push } from 'react-router-redux';
import {Provider} from 'react-redux';
import createHistory from 'history/createBrowserHistory';
import App from './pages/App.jsx';
// Export React so the dev tools can find it
if (window === window.top) {
window.React = React;
}
const history = createHistory();
const reduxLoggerMiddleware = createLogger();
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
combineReducers({
...reducers,
router: routerReducer
}),
applyMiddleware(thunkMiddleware,
sagaMiddleware,
reduxLoggerMiddleware,
routerMiddleware(history))
);
// create a store with middlewares
sagaMiddleware.run(sagas);
ReactDOM.render(
<Provider store={store}>
<BrowserRouter history='history'>
<Route path="/" component={App} />
</BrowserRouter>
</Provider>,
document.getElementById('app')
);
App.jsx where the mapStateToProps is undefined
import React from 'react';
import PropTypes from 'prop-types';
import { render } from 'react-dom';
import Header from '../components/Header.jsx';
import {getProfile} from '../redux/actions/ProfileActions';
import UserSession from '../UserSession';
import Footer from '../components/Footer.jsx';
import Breadcrumbs from '../components/Breadcrumbs.jsx';
import NotificationContainer from '../components/common/NotificationContainer.jsx';
import rootReducer from '../redux/reducers/index';
import { BrowserRouter, Route, browserHistory } from 'react-router-dom';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import {getWhisky, getRegions, getWhiskiesByDistillery} from '../redux/actions/WhiskyActions';
import Routes from '../routes/Routes';
class App extends React.Component {
constructor() {
super();
}
componentWillMount() {
if (UserSession.getToken() && !this.props.profile) {
this.props.dispatch(getProfile());
}
}
render() {
var {auth, profile, routes, whisky, distillery, params} = this.props;
return (
<div className="nt-app">
<Header auth={auth}
profile={profile}/>
<Breadcrumbs routes={routes}
params={params}
whisky={whisky}
distillery={distillery}/>
<div className="nt-app-page">
<Routes />
</div>
<Footer />
<NotificationContainer />
</div>
);
}
}
App.displayName = 'App';
App.propTypes = {
routes: PropTypes.array,
params: PropTypes.object,
children: PropTypes.object,
whisky: PropTypes.object,
distillery: PropTypes.object
};
function mapStateToProps(state) {
return {
whisky: state.whiskies.detail,
distillery: state.distillery.detail,
auth: state.auth,
profile: _.get(state.profile, 'profile', null)
};
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actionCreators, dispatch) }
}
export default connect(mapStateToProps)(App);
The relevant reducer
import * as Types from '../actions/WhiskyActionTypes';
const initialState = {
isFetchingFeatured: false,
isFetchingByRegion: false,
isFetching: false,
featured: [],
byRegion: {},
detail: null
};
export default function whiskies(state = initialState, action) {
switch (action.type) {
case 'SET_STATE': return state.merge(action.state);
case Types.WHISKIES_FEATURED_GET_REQUEST:
return {
...state,
isFetchingFeatured: true,
isFetching: true
};
case Types.WHISKIES_FEATURED_GET_SUCCESS:
return {
...state,
isFetchingFeatured: false,
isFetching: getIsFetching(false),
featured: action.response
};
case Types.WHISKIES_BY_REGION_GET_REQUEST:
return {
...state,
isFetchingByRegion: true,
isFetching: true
};
case Types.WHISKIES_BY_REGION_GET_SUCCESS:
return {
...state,
isFetchingByRegion: false,
isFetching: getIsFetching(false),
byRegion: action.response
};
case Types.WHISKIES_BY_DISTILLERY_GET_REQUEST:
return {
...state,
isFetchingByDistillery: true,
isFetching: true
};
case Types.WHISKIES_BY_DISTILLERY_GET_SUCCESS:
return {
...state,
isFetchingByDistillery: false,
isFetching: getIsFetching(false),
byDistillery: action.response
};
case Types.WHISKY_DETAIL_GET_REQUEST:
return {
...state,
isFetching: true
};
case Types.WHISKY_DETAIL_GET_SUCCESS:
return {
...state,
isFetching: false,
detail: action.response
};
case Types.WHISKY_DETAIL_CLEAR:
return {
...state,
detail: null
};
default:
return state;
}
}
function getIsFetching(state, isFetching) {
return (state.isFetchingByRegion || state.isFetchingFeatured || isFetching);
}
the saga
import {call, put} from 'redux-saga/effects';
import {takeEvery} from 'redux-saga';
import WhiskyApi from '../../api/WhiskiesApi';
import * as Actions from '../actions/WhiskyActions';
import * as Types from '../actions/WhiskyActionTypes';
export default function* whiskyFlow() {
yield [
takeEvery(Types.WHISKY_DETAIL_GET_REQUEST, getWhisky),
takeEvery(Types.WHISKIES_RELATED_GET_REQUEST, getRelatedWhiskies),
takeEvery(Types.WHISKIES_FEATURED_GET_REQUEST, getFeaturedWhiskies)
];
}
function* getWhisky(action) {
var {id} = action;
try {
const response = yield call(WhiskyApi.getWhisky, id);
yield put(Actions.getWhiskySuccess(response));
}
catch (error) {
yield put(Actions.getWhiskyFailure(error));
}
}
function* getRelatedWhiskies(action) {
var {id} = action;
try {
const response = yield call(WhiskyApi.getRelated, id);
yield put(Actions.getRelatedWhiskiesSuccess(response));
}
catch (error) {
yield put(Actions.getRelatedWhiskiesFailure(error));
}
}
function* getFeaturedWhiskies(action) {
var {id} = action;
try {
const response = yield call(WhiskyApi.getFeatured, id);
yield put(Actions.getFeaturedWhiskiesSuccess(response));
}
catch (error) {
yield put(Actions.getFeaturedWhiskiesFailure(error));
}
}
I really appreciate all the support everyone offers on S.O. and hope someone might have a solution