Reputation: 17269
I'm trying to set up protected routes using react router v4.
I have the following, which works fine.
function PrivateRoute({ component: Component, authed, ...rest }) {
return (
<Route
{...rest}
render={props =>
authed === true ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: '/', state: { from: props.location } }} />
)
}
/>
);
}
However, when I change this code to:
type PrivateRouteProps = {
component: Component,
};
const PrivateRoute = ({ component, authed, ...rest }: PrivateRouteProps) => (
<Route
{...rest}
render={props =>
authed === true ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: '/', state: { from: props.location } }} />
)
}
/>
);
I get the error: TypeError: instance.render is not a function
. When I change to (note the component: Component
), everything works:
const PrivateRoute = ({ component: Component, authed, ...rest }) => (
<Route
{...rest}
render={props =>
authed === true ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: '/', state: { from: props.location } }} />
)
}
/>
);
The render function of App.js
is as follows:
render() {
return this.state.loading === true ? (
'loading'
) : (
<BrowserRouter>
<div>
{this.renderHeader()}
<Switch>
<Route exact path="/" component={LandingPage} />
<PrivateRoute
authed={this.state.authed}
path="/home"
component={HomePage}
/>
<Route component={PageNotFoundPage} />
</Switch>
</div>
</BrowserRouter>
);
}
Why does the arrow function with PrivateRouteProps
not work as expected?
Upvotes: 1
Views: 2100
Reputation: 16354
In your second example you try to render the component you passed in with
<Component {...props}/>
From the way you defined your flow type I guess that you imported Component
like this:
import React, {Component} from 'react'.
That means that Component
does not refer to the component passed in the component
prop but still to the class Component
imported from react
because you did not shadow it anywhere inside your functional component. Even if you imported Component
in your first example it would still work because you shadowed the name Component
with the value of the prop component
. In the second example you did not do that.
This is why you get the error because the react class Component
neither has a render()
method nor does it have any other functionality you expected there.
You need to assign the prop component
to another name that is capitalized, e.g. Node
and then render that variable. Note that the name needs to be capitalized. Otherwise it will be interpreted as usual html node and not as a react component:
type PrivateRouteProps = {
component: Component,
};
const PrivateRoute = ({ component: Node /* assign it to a capitalized name */, authed, ...rest }: PrivateRouteProps) => (
<Route
{...rest}
render={props =>
authed === true ? (
{/* Do not use `Component` here as it refers to the class imported from react and not to your component */}
<Node {...props} />
) : (
<Redirect to={{ pathname: '/', state: { from: props.location } }} />
)
}
/>
);
You could of course also use Component
as a name which will shadow the name Component
from the outer scope but this is bad practice as it often leads to confusion.
Upvotes: 3