从库导入时,Invariant 失败:您不应使用 <Route>在 &lt;Router&gt; 之外 [英] When importing from a library, Invariant failed: You should not use &lt;Route&gt; outside a &lt;Router&gt;

查看:48
本文介绍了从库导入时,Invariant 失败:您不应使用 <Route>在 &lt;Router&gt; 之外的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有很多关于错误:不变失败:你不应该在 之外使用 "的问题,但是这个一个不同之处在于它仅在使用库中返回 的组件时发生.

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> 组件如果位于正在使用它的应用程序中的某个位置,它就可以正常工作.但是,如果同一个组件在库中,并且应用程序 imports 从库而不是从它自己的项目目录结构,那么我得到:

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 imports <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>在 &lt;Router&gt; 之外的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆