J'essayais de créer un <PrivateRoute>
comme décrit dans le react-router documents en utilisant TypeScript. Quelqu'un peut m'aider?
Le privateRoute dans le document react-router:
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
fakeAuth.isAuthenticated ? (
<Component {...props}/>
) : (
<Redirect to={{pathname: '/login', state: { from: props.location }
}}/>
)
)}/>
)
Ci-dessous ma version TypeScript (cela ne fonctionnera pas):
const PrivateRoute = (theProps: { path: string, component: React.SFC<RouteComponentProps<any> | undefined> | React.ComponentClass<RouteComponentProps<any> | undefined> }) => {
return <Route path={theProps.path} render={props => (
fakeAuth.isAuthenticated ? (
<React.Component {...theProps} /> <!-- **** It will raise error *** -->
) : (
<Redirect to={{
pathname: '/',
state: { from: props.location }
}} />
)
)} />
}
Le <React.Component {...thisProps} />
ce n'est pas correct. L'erreur est: NodeInvocationException: inst.render n'est pas une fonction TypeError: inst.render n'est pas une fonction
L'erreur a probablement à voir avec le typage et le retour implicite dans le rendu. Lorsque vous corrigez cela, vous obtenez finalement quelque chose comme ceci:
const PrivateRoute = ({component, isAuthenticated, ...rest}: any) => {
const routeComponent = (props: any) => (
isAuthenticated
? React.createElement(component, props)
: <Redirect to={{pathname: '/login'}}/>
);
return <Route {...rest} render={routeComponent}/>;
};
Ce composant peut être utilisé comme ceci:
<PrivateRoute
path='/private'
isAuthenticated={this.props.state.session.isAuthenticated}
component={PrivateContainer}
/>
Il y a quelques inconvénients avec la solution ci-dessus. L'un d'eux est que vous perdez la sécurité du type.
Étendre probablement le composant Route
est la meilleure idée.
import * as React from 'react';
import {Redirect, Route, RouteProps} from 'react-router';
export interface ProtectedRouteProps extends RouteProps {
isAuthenticated: boolean;
authenticationPath: string;
}
export class ProtectedRoute extends Route<ProtectedRouteProps> {
public render() {
let redirectPath: string = '';
if (!this.props.isAuthenticated) {
redirectPath = this.props.authenticationPath;
}
if (redirectPath) {
const renderComponent = () => (<Redirect to={{pathname: redirectPath}}/>);
return <Route {...this.props} component={renderComponent} render={undefined}/>;
} else {
return <Route {...this.props}/>;
}
}
}
Vous pouvez donc utiliser le composant comme ceci:
const defaultProtectedRouteProps: ProtectedRouteProps = {
isAuthenticated: this.props.state.session.isAuthenticated,
authenticationPath: '/login',
};
<ProtectedRoute
{...defaultProtectedRouteProps}
exact={true}
path='/'
component={ProtectedContainer}
/>
Si vous préférez écrire des composants fonctionnels, vous pouvez le faire de manière très similaire. Cela fonctionne également avec React Router 5:
import * as React from 'react';
import { Redirect, Route, RouteProps } from 'react-router';
export interface ProtectedRouteProps extends RouteProps {
isAuthenticated: boolean;
isAllowed: boolean;
restrictedPath: string;
authenticationPath: string;
}
export const ProtectedRoute: React.FC<ProtectedRouteProps> = props => {
let redirectPath = '';
if (!props.isAuthenticated) {
redirectPath = props.authenticationPath;
}
if (props.isAuthenticated && !props.isAllowed) {
redirectPath = props.restrictedPath;
}
if (redirectPath) {
const renderComponent = () => <Redirect to={{ pathname: redirectPath }} />;
return <Route {...props} component={renderComponent} render={undefined} />;
} else {
return <Route {...props} />;
}
};
export default ProtectedRoute;
Si vous souhaitez rediriger un utilisateur vers le chemin auquel l'utilisateur voulait accéder en premier, vous devez vous souvenir du chemin, afin de pouvoir rediriger après une authentification réussie. La réponse suivante vous guidera à travers cela:
Vous pouvez toujours utiliser le formulaire SFC, que je trouve un peu plus propre. Mélangez simplement tous les accessoires dont vous avez besoin avec le RouteProps
:
const PrivateRoute: React.SFC<RouteProps> = ({
component: Component,
...rest
}: {
component: React.ComponentType<RouteProps>;
}) => (
<Route
{...rest}
render={props =>
fakeAuth.isAuthenticated
? <Component {...props} />
: <Redirect to="/login" />
}
/>
);