supasate / connected-react-router

A Redux binding for React Router v4
MIT License
4.73k stars 593 forks source link

Uncaught TypeError: Cannot read property 'push' of undefined!!!! #366

Open dcs3spp opened 5 years ago

dcs3spp commented 5 years ago

Hi,

I am a new user struggling with migrating from react-router to connected-react-router. I am receiving an error:

Uncaught TypeError: Cannot read property 'push' of undefined

when trying to redirect to another page. The redirect happens but I am receiving this error in the console.

Currently, my ErrorBoundary component is still using react-router and is initialised using withRouter. Not sure if that would cause the error??

Where am I incorrectly configuring connected-react-router or is this a bug?

Config is taking too long. I have now managed to get react-router working with withRouter and can access history, current path etc. that way....

Functional component

import React, { useEffect } from 'react';

import { connect } from 'react-redux';
import Grid from '@material-ui/core/Grid';
import { GridSpacing } from '@material-ui/core/Grid';

import Course from '../components/Course/Course';

import { courseModels } from '../redux/features/course';
import { courseSelectors } from '../redux/features/course';
import { fetchCoursesAsync } from '../redux/features/course/actions';
import { RootState } from 'ReduxTypes';

import { push } from 'connected-react-router';

type ErrorReport = { hasError: boolean; error?: Error };
type StateProps = {
  isLoading: boolean;
  courses: courseModels.Course[];
  error: ErrorReport;
};

/**
 * Redux dispatch and state mappings
 */
const dispatchProps = {
  fetchCourses: fetchCoursesAsync.request,
  history: { push },
};

const mapStateToProps = (state: RootState): StateProps => ({
  isLoading: state.courses.isLoadingCourses,
  courses: courseSelectors.getReduxCourses(state.courses),
  error: courseSelectors.getReduxCoursesError(state.courses),
});

/**
 * Component property type definitions
 */
type Props = ReturnType<typeof mapStateToProps> & typeof dispatchProps;

/**
 * CourseList component
 */
const CourseList = ({
  courses = [],
  error,
  fetchCourses,
  history,
  isLoading,
}: Props): JSX.Element => {
  // fetch course action on mount
  useEffect(() => {
    console.log('COURSELIST FETCHING COURSES');
    fetchCourses();
  }, [fetchCourses]);

  if (isLoading) {
    return <p>Loading...</p>;
  }

 // THIS IS CAUSING THE ERROR, REDIRECT HAPPENS BUT ERROR MESSAGE IS 
// DISPLAYED IN CONSOLE
  history.push('/error');

  return (
    <div style={{ marginTop: 20, padding: 30 }}>
      {
        <Grid container spacing={2 as GridSpacing} justify="center">
          {courses.map(element => (
            <Grid item key={element.courseID}>
              <Course course={element} />
            </Grid>
          ))}
        </Grid>
      }
    </div>
  );
};

/**
 * Exports
 */
export default connect(
  mapStateToProps,
  dispatchProps,
)(CourseList);

index.ts for store module

import { RootAction, RootState } from 'ReduxTypes';
import { createStore, applyMiddleware } from 'redux';
import { createEpicMiddleware } from 'redux-observable';
import { createBrowserHistory } from 'history';
import { routerMiddleware as createRouterMiddleware } from 'connected-react-router';

import { composeEnhancers } from './utils';
import rootReducer from './rootReducer';
import rootEpic from './rootEpic';
import services from '../../services';

// browser history
export const history = createBrowserHistory();

export const epicMiddleware = createEpicMiddleware<
  RootAction,
  RootAction,
  RootState
>({
  dependencies: services,
});

const routerMiddleware = createRouterMiddleware(history);

// configure middlewares
const middlewares = [epicMiddleware, routerMiddleware];

// compose enhancers
const enhancer = composeEnhancers(applyMiddleware(...middlewares));

// rehydrate state on app start
const initialState = {};

// create store
const store = createStore(rootReducer(history), initialState, enhancer);

epicMiddleware.run(rootEpic);

// export store singleton instance
export default store;

Routing component

import React, { Component, Suspense, lazy } from 'react';

import { Route, Switch } from 'react-router';
import { history } from '../redux/store';
import ErrorPage from '../errors/ErrorPage';
import ErrorBoundary from '../errors/ErrorBoundary';
import { NavBar } from './NavBar/NavBar';

import { ConnectedRouter } from 'connected-react-router';

// lazy loading components and other stuff here, skipping ahead....
type AppProps = {};

export class App extends Component<AppProps, {}> {
  public render(): JSX.Element {
    return (
      <ConnectedRouter history={history}>
        <div>
          <NavBar />
          <Suspense fallback={<div>LoaderOptionsPlugin...</div>}>
            <Switch>
              <Route path="/" component={Home} exact></Route>
              <Route path="/about" component={About}></Route>
              <Route
                path="/courses"
                render={(props): JSX.Element => (
                  <ErrorBoundary {...props}>
                    <CourseList {...this.props} />
                  </ErrorBoundary>
                )}
              ></Route>
              <Route path="/error" component={ErrorPage}></Route>
              {/* <Route component={Error404}></Route> */}
            </Switch>
          </Suspense>
        </div>
      </ConnectedRouter>
    );
  }
}

Root reducer

import { combineReducers } from 'redux';

import { History } from 'history';

import coursesReducer from '../features/course/reducer';
import { connectRouter } from 'connected-react-router';

const rootReducer = (history: History) =>
  combineReducers({
    router: connectRouter(history),
    courses: coursesReducer,
  });

export default rootReducer;

Dependencies

"dependencies": {
   ...
    "connected-react-router": "6.5.2",
    "react": "16.10.1",
    "react-dom": "16.10.1",
    "react-redux": "7.1.1",
    "react-router": "5.1.2",
    "react-router-dom": "5.1.2",
    "redux": "4.0.4",
    "redux-observable": "1.2.0",
   ...
  }
abdeldjalilhachimi commented 4 years ago

I think, you should import {withRouter{ from react router

try this myabe you find it helpful

import { withRouter } from 'react-router';

class yourClassNAme extends Component { ... }

export default withRouter(yourClassNAme );

grekulanssi commented 4 years ago

I think, you should import {withRouter{ from react router try this myabe you find it helpful

import { withRouter } from 'react-router';

class yourClassNAme extends Component { ... }

export default withRouter(yourClassNAme );

I'm having a similar issue, but when I'm trying to do that on my App.js, I get: Error: Invariant failed: You should not use <withRouter(App) /> outside a <Router>

gautamsolankidev commented 4 years ago

I think, you should import {withRouter{ from react router try this myabe you find it helpful import { withRouter } from 'react-router'; class yourClassNAme extends Component { ... } export default withRouter(yourClassNAme );

I'm having a similar issue, but when I'm trying to do that on my App.js, I get: Error: Invariant failed: You should not use <withRouter(App) /> outside a <Router>

Found any solution? I'm having exact same issue