Reputation: 95
I am using react router v4 and I'm trying to wrap my head around a react-router / redux / HOC related issue. I have a higher order component working. The HOC itself is connect()
-ed to redux store. This approach works perfectly if I wire it up in a <Route />
via a component
prop: <Route path="/profile" component={ withAuth(Profile) } />
does work.
However, when I try to do the same with a <Route />
and a render prop it does not work: <Route path="/profile" render={ () => withAuth(Profile) } />
The console throws "Route.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
" It does work when I omit the HOC: <Route path="/profile" render={ () => <Profile /> } />
so I suspect a problem with the HOC but I can't find it.
The reason I'm trying to use render
is I'd like to pass additional props to the HOC. Besides it bugs me that I can't find the bug.
Can anybody with a fresh eye have a look and put me on the right path? Thanks!
/* === app.js === */
import React, { Component } from 'react';
import { Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import Header from './header';
import Home from './home';
import Content from './about';
import Profile from './profile';
import withAuth from './withAuth';
import store from '../reducers/store';
export default class App extends Component {
render() {
return (
<Provider store={store}>
<div className="mdl-grid">
<Header />
<main className="mdl-layout__content">
<div className="page-content">
<Route path="/" exact component={Home} />
<Route path="/about" component={Content} />
<Route path="/profile" render={ () => withAuth(Profile) } />
</div>
</main>
</div>
</Provider>
)
}
}
/* === withAuth.js (Higher order component) === */
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';
const HOC = WrappedComponent => {
return class extends Component {
render() {
if (this.props.auth) {
return <WrappedComponent authenticated={this.props.auth} {...this.props} />
} else {
return <Redirect to="/" />
}
}
}
}
function mapStateToProps({ auth }) {
return { auth };
}
export default WrappedComponent => connect(mapStateToProps)( HOC(WrappedComponent) );
Upvotes: 2
Views: 1263
Reputation: 1236
I think your problem is with the way you use <Redirect />
, you have to put it in <Route />
. Look at this example:
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
fakeAuth.isAuthenticated ? (
<Component {...props}/>
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location }
}}/>
)
)}/>
)
Upvotes: 0
Reputation: 281626
The reason it doesn't work is because, here
<Route path="/profile" render={ () => withAuth(Profile) } />
render is actually assigned a function withAuth and not the returned value. What you need to do is
const AuthProfile = withAuth(Profile);
export default class App extends Component {
render() {
return (
<Provider store={store}>
<div className="mdl-grid">
<Header />
<main className="mdl-layout__content">
<div className="page-content">
<Route path="/" exact component={Home} />
<Route path="/about" component={Content} />
<Route path="/profile" render={ (props) => <AuthProfile {...props}/> } />
</div>
</main>
</div>
</Provider>
)
}
}
The difference between
render={ () => withAuth(Profile) }
and
render={ (props) => <AuthProfile {...props}/> }
is that in the first case its an arrow function that is bound to the context. Whereas in the second case its a function returning a component
Upvotes: 3