Following this tutorial: https://reacttraining.com/react-router/web/example/auth-workflow.
Trying to reproduce the code:
const PrivateRoute = ({ component: Component, ...rest }) => ( <Route {...rest} render={props => fakeAuth.isAuthenticated ? ( <Component {...props} /> ) : ( <Redirect to={{ pathname: "/login", state: { from: props.location } }} /> ) } /> );
In TypeScript:
import * as React from 'react'; import { Route, RouterProps } from 'react-router'; interface Props extends RouterProps { component: React.Component; } const PrivateRoute = ({ component: Component, ...rest }: Props) => { return ( <Route {...rest} render={(props) => <Component {...props} />} /> ); }; export default PrivateRoute;
But it would always fail. Tried different variations. The one I've posted the most recent one. Getting:
It seems to me that I have to pass Generic for the Component type, but I don't know how.
EDIT:
The closest solution so far:
interface Props extends RouteProps { component: () => any; } const PrivateRoute = ({ component: Component, ...rest }: Props) => { return ( <Route {...rest} render={(props) => <Component {...props} />} /> ); };
And then:
<PrivateRoute component={Foo} path="/foo" />
You can pass a component as props in React by using the built-in children prop. All elements you pass between the opening and closing tags of a component get assigned to the children prop. Copied!
Approach 1 - React. We are passing a simple div to the HOC. Div, span, p, h1 or any other usual DOM elements are only allowed to accept their usual properties like id, className, role, etc, which makes sense. They can't be given custom props unless we use data-attributes.
You want to use JSX inside your props You can simply use {} to cause JSX to parse the parameter. The only limitation is the same as for every JSX element: It must return only one root element.
You want to pass a component constructor, not a component instance:
import * as React from 'react'; import { Route, RouteProps } from 'react-router'; interface Props extends RouteProps { component: React.ComponentType; } const PrivateRoute = ({ component: Component, ...rest }: Props) => { return ( <Route {...rest} render={(props) => <Component {...props} />} /> ); }; export default PrivateRoute; class Foo extends React.Component { } let r = <PrivateRoute component={Foo} path="/foo" />
Edit
A more complete solution should be generic and use RouteProps
instead RouterProps
:
import * as React from 'react'; import { Route, RouteProps } from 'react-router'; type Props<P> = RouteProps & P & { component: React.ComponentType<P>; } const PrivateRoute = function <P>(p: Props<P>) { // We can't use destructuring syntax, because : "Rest types may only be created from object types", so we do it manually. let rest = omit(p, "component"); let Component = p.component; return ( <Route {...rest} render={(props: P) => <p.component {...props} />} /> ); }; // Helpers type Diff<T extends string, U extends string> = ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T]; type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>; function omit<T, TKey extends keyof T>(value:T, ... toRemove: TKey[]): Omit<T, TKey>{ var result = Object.assign({}, value); for(let key of toRemove){ delete result[key]; } return result; } export default PrivateRoute; class Foo extends React.Component<{ prop: number }>{ } let r = <PrivateRoute component={Foo} path="/foo" prop={10} />
After a few hours and some investigation, here is the solution that fits my requirements:
import * as React from 'react'; import { Route, RouteComponentProps, RouteProps } from 'react-router'; const PrivateRoute: React.SFC<RouteProps> = ({ component: Component, ...rest }) => { if (!Component) { return null; } return ( <Route {...rest} render={(props: RouteComponentProps<{}>) => <Component {...props} />} /> ); }; export default PrivateRoute;
any
;If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With