mycellius
mycellius

Reputation: 598

Unable to use "connected" HOC with react router

I'm making a HOC that I can use to ensure logged in users don't visit certain routes (what I call "guest routes"). It will be connected to my redux store to access the user's loggedIn state. Here is my implementation of the HOC:

import React, { Component } from 'react';
import { connect } from 'react-redux';

export function guestRoute(ComposedComponent) {
  class GuestRoute extends Component {
    componentWillMount() {
      if (this.props.loggedIn) {
        this.props.router.push('/')
      }
    }

    componentWillUpdate() {
      if (this.props.loggedIn) {
        this.props.router.push('/')
      }
    }

    render() {
      return (
        <ComposedComponent {...this.props} />
      );
    }
  }

  const mapStateToProps = (state) => {
    const { loggedIn } = state.auth;
    return { loggedIn };
  };

  return connect(mapStateToProps)(GuestRoute);
}

As you can see, I'm returning a "connected" wrapper component.

Here are my routes and how this HOC is being used:

import React from 'react';
import { Route } from 'react-router';
import { guestRoute } from './routes/guestRoute';
import Main from './layout/Main';
import Signup from './auth/Signup';

const routes = (
  <Route component={Main}>
    <Route path="/signup" component={guestRoute(Signup)} />
  </Route>
);

export default routes;

This gives me two errors:

Warning: Failed prop type: Invalid prop `component` supplied to `Route`.

and

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: symbol.

How can I go about returning a valid component that I can use in my route instead of what's returned by connect?

EDIT: I'm using react v16.13.1 and react-router v3.2.1

Here is a sandbox that reproduces the error: https://codesandbox.io/s/zealous-austin-vy2et?file=/src/store.js

Upvotes: 0

Views: 100

Answers (1)

Zachary Haber
Zachary Haber

Reputation: 11017

I got your codesandbox working, the main thing I did was update the react-router to the latest of react-router-dom. I'm not sure why you are using a new project with an ancient router library.

Other things I did were to make the code run, are I made the app declaration be an actual component rather than static JSX which causes issues

const App = () => (
  <React.StrictMode>
    <Provider store={store}>
      <Router>{routes}</Router>
    </Provider>
  </React.StrictMode>
);

ReactDOM.render(<App />, document.getElementById("root"));

I also made your routes have a bit of a difference in declaration as the Route doesn't support both children and a component at the same time. Since the Route values weren't needed in Main, it can be changed to being a child of the route.

const routes = (
  <>
    <Route path="/">
      <Main>
        <Route path="/signup" component={Signup} />
      </Main>
    </Route>
  </>
);

In Guest Route, I changed componentWillMount and componentWillUpdate to componentDidMount and componentDidUpdate as they are the preferred options as the others are now deprecated.

In Signup, I exported it with the guestRoute HOC around it

export default guestRoute(
  connect(
    null,
    mapDispatchToProps
  )(Signup)
);

https://codesandbox.io/s/inspiring-field-yokx4?file=/src/layout/Main.jsx

Upvotes: 1

Related Questions