从库导入时,Invariant 失败:您不应使用 <Route>在 <Router> 之外 [英] When importing from a library, Invariant failed: You should not use <Route> outside a <Router>
问题描述
有很多关于错误:不变失败:你不应该在
之外使用
"的问题,但是这个一个不同之处在于它仅在使用库中返回
的组件时发生.
There are a number of questions out there for "Error: Invariant failed: You should not use <Route>
outside a <Router>
", but this one differs in that it only occurs when using a component from a library that returns a <Route>
.
我的目的是在我公司的其他人可以使用 npm
安装的私有库中创建一个
组件.这个新组件返回一个
,但首先检查谓词的返回值;如果检查失败,
将指向某个替代页面组件.一个简单的用例是检查用户是否经过身份验证.如果是这样,component
中的任何内容都会被渲染;如果没有,将呈现替代页面组件,即登录屏幕.
My intent is to create a <GuardedRoute>
component in a private library that others at my company can install using npm
. This new component returns a <Route>
, but checks the return value of a predicate first; if the check fails, the <Route>
will point to some alternative page component. A simple use case is to check whether the user is authenticated. If so, whatever is in component
will get rendered; if not, the alternative page component, a login screen, will be rendered.
<GuardedRoute>
组件如果位于正在使用它的应用程序中的某个位置,它就可以正常工作.但是,如果同一个组件在库中,并且应用程序 import
s
从库而不是从它自己的项目目录结构,那么我得到:
The <GuardedRoute>
component works just fine if it is sitting somewhere in the app that is using it. However, if that same component is in a library, and the app import
s <GuardedRoute>
from the library instead of from its own project directory structure, then I get:
Error: Invariant failed: You should not use <Route> outside a <Router>
堆栈跟踪没有多大帮助;最新的相关部分是在 index.tsx
中抛出 ReactDOM.render()
.
The stack trace isn't much help; the most recent relevant piece of it is throwing on ReactDOM.render()
in index.tsx
.
库被编译成 JS,然后使用 npm i path/to/library/on/my/filesystem
安装到应用程序中.
The library is compiled into JS and then installed into the app using 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
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(位于库中)
GuardedRoute.tsx (located in the library)
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} />
);
};
推荐答案
我的猜测是您的库代码抛出错误,因为它没有将路由器组件视为父组件.
My guess is that your library code is throwing the error because it doesn't see a router component as the parent.
也许您可以创建一个 GuardedComponent
来包装传递的 Component
而不是扩展路由?此代码未经测试,仅用于一般概念.
Perhaps you can create a GuardedComponent
that wraps the passed Component
rather than extending the route? This code is untested but just for a general idea.
interface IGuardedComponentProps {
children: React.ReactNode
fnGuard: () => boolean;
elFailure: JSX.Element;
}
export const GuardedComponent: React.FC<IGuardedComponentProps> = props => {
const { children, fnGuard, elFailure } = props;
return (
<>{fnGuard() ? elFailure : children}</>
);
};
然后你会像这样使用它
<Route
exact
path="/"
>
<GuardedComponent
fnGuard={() => true}
elFailure={(
<Redirect to="/auth" />
)} />
>
<ReportDirectoryActivity />
</GuardedComponent>
</Route>
这篇关于从库导入时,Invariant 失败:您不应使用 <Route>在 <Router> 之外的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!