Существует ряд вопросов для «Ошибка: сбой инварианта: вы не должны использовать <Route>
вне <Router>
», но этот отличается тем, что он возникает только при использовании компонента из библиотеки, которая возвращает <Route>
.
Я намереваюсь создать компонент <GuardedRoute>
в частной библиотеке, который другие сотрудники моей компании могут установить с помощью npm
. Этот новый компонент возвращает <Route>
, но сначала проверяет возвращаемое значение предиката; если проверка не пройдена, <Route>
будет указывать на какой-либо альтернативный компонент страницы. Простой вариант использования - проверить, аутентифицирован ли пользователь. Если так, то все, что находится в component
, будет обработано; в противном случае будет отображен альтернативный компонент страницы, экран входа в систему.
Компонент <GuardedRoute>
работает отлично, если он находится где-то в приложении, которое его использует. Однако, если этот же компонент находится в библиотеке, а приложение import
s <GuardedRoute>
из библиотеки, а не из его собственной структуры каталогов проекта, то я получаю:
Error: Invariant failed: You should not use <Route> outside a <Router>
Трассировка стека isn не очень помогает; последняя актуальная часть этого кода - ReactDOM.render()
в index.tsx
.
. Библиотека компилируется в JS, а затем устанавливается в приложение с использованием индекса npm i path/to/library/on/my/filesystem
.
. .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 (находится в библиотеке)
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} />
);
};