mapStateToProps undefined - react router 4, redux and saga

时间:2017-06-19 13:54:26

标签: reactjs react-router react-redux redux-saga

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

0 个答案:

没有答案