Hay una serie de preguntas para "Error: Invariante fallido: no debe usar <Route>
fuera de a <Router>
", pero esta se diferencia en que solo ocurre cuando se usa un componente de una biblioteca que devuelve a <Route>
.
Mi intención es crear un <GuardedRoute>
componente en una biblioteca privada que otros en mi empresa puedan instalar usando npm
. Este nuevo componente devuelve un <Route>
, pero comprueba primero el valor de retorno de un predicado; si la comprobación falla, <Route>
apuntará a algún componente de página alternativo. Un caso de uso simple es verificar si el usuario está autenticado. Si es así, todo lo que esté en component
se renderizará; de lo contrario, se representará el componente de página alternativo, una pantalla de inicio de sesión.
El <GuardedRoute>
componente funciona bien si está ubicado en algún lugar de la aplicación que lo está usando. Sin embargo, si ese mismo componente está en una biblioteca y la aplicación import
es <GuardedRoute>
de la biblioteca en lugar de su propia estructura de directorio de proyecto, obtengo:
Error: Invariant failed: You should not use <Route> outside a <Router>
El seguimiento de la pila no es de mucha ayuda; la pieza relevante reciente mayor parte de ella está tirando de ReactDOM.render()
en index.tsx
.
La biblioteca se compila en JS y luego se instala en la aplicación usando npm i path/to/library/on/my/filesystem
.
index.tsx
import * as React from 'react';
import ReactDOM from 'react-dom';
import { App } from './App';
import './index.css';
function __init() {
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
}
__init();
App.tsx
import * as React from 'react';
import {
Route,
BrowserRouter,
Switch
} from 'react-router-dom';
import { ReportDirectoryActivity } from 'components/activities/ReportDirectoryActivity/ReportDirectoryActivity';
import { AuthActivity } from 'components/activities/AuthActivity/AuthActivity';
import { LogoutActivity } from 'components/activities/LogoutActivity/LogoutActivity';
import { PrivateRoute } from 'components/shared/PrivateRoute/PrivateRoute';
export const App: React.FC = () => {
return (
<BrowserRouter>
<Switch>
<Route
exact
path="/auth"
component={AuthActivity} />
<Route
exact
path="/logout"
component={LogoutActivity} />
<PrivateRoute
exact
path="/"
component={ReportDirectoryActivity} />
</Switch>
</BrowserRouter>
);
};
PrivateRoute.tsx
import * as React from 'react';
import {
RouteProps,
Redirect
} from 'react-router-dom';
// if this line is changed to refer to an identical component within the app, this works fine
import { GuardedRoute } from 'my-library/GuardedRoute';
export interface IPrivateRouteProps extends RouteProps {}
export const PrivateRoute: React.FC<IPrivateRouteProps> = props => {
// using a pass-through fnGuard() just to test
return (
<GuardedRoute
{...props}
fnGuard={() => true}
elFailure={(
<Redirect to="/auth" />
)} />
);
};
GuardedRoute.tsx (ubicado en la biblioteca)
import * as React from 'react';
import _ from 'lodash';
import {
Route,
RouteProps
} from 'react-router-dom';
export interface IGuardedRouteProps extends RouteProps {
fnGuard: () => boolean;
elFailure: JSX.Element;
}
export const GuardedRoute: React.FC<IGuardedRouteProps> = props => {
const restProps = _.cloneDeep(props);
delete restProps.fnGuard;
delete restProps.elFailure;
const Component = props.component;
function renderComponent(renderProps: any) {
return Component ? (
<Component {...renderProps} />
) : null;
}
return (
<Route
{...restProps}
render={renderProps => props.fnGuard() ?
renderComponent(renderProps) :
props.elFailure} />
);
};