如何在外部 npm 包中使用 React 的 Link 组件而不会出现错误:不变的“你不应该在路由器外部使用链接”

Posted

技术标签:

【中文标题】如何在外部 npm 包中使用 React 的 Link 组件而不会出现错误:不变的“你不应该在路由器外部使用链接”【英文标题】:How do I use React's Link component in an external npm package without error: invariant "you should not use link outside router" 【发布时间】:2020-03-15 21:09:02 【问题描述】:

我创建了一个呈现图标菜单的 NPM 包(将在各种项目中使用)。允许用户向该图标菜单发送“路线”道具。因此,每个图标都可以是指向不同页面的链接。

但是,当我导入我的包时,我不断收到以下错误:

错误:不变量失败:您不应在 <Router> 之外使用 <Link>

即使我在项目的路由器中导入和渲染图标菜单。或者,在这种情况下,是 BrowserRouter。

因为这是一个巨大的项目,所以我很快将这个过程分为两部分进行了重新创建。 请记住,该包是一个 NPM 包。如果它是我项目中的一个组件,那将不是问题。但是,我在不同的、相同的项目中需要这个包。

第 1 部分:NPM 包

IconMenu.js

import React from 'react';
import Icon from '@IDB/react-iconlibrary';
import  Link  from 'react-router-dom';

const IconMenu = ( items ) => 
    return (
        <div className='icon_menu'>
            items.map((item, index) => (
                <Link to=item.route>
                    <Icon className='icon_box_icon' key=index name=item.icon title=item.name />
                </Link>
            ))
        </div>
    )

export default IconMenu 

第 2 部分:项目

Project.js

import  IconMenu  from '@IDB/ui-elements';
import React,  Component  from 'react';
import  BrowserRouter, Route  from 'react-router-dom';

export default class Project extends Component  
    render () 
        return (
            <BrowserRouter>
                <div id='page'>
                    <Route path='/' render=(props) => (
                        <IconMenu 
                            items=[
                                
                                    icon: 'Edit',
                                    name: 'Edit page',
                                    route: '/edit'
                                ,
                                
                                    icon: 'View',
                                    name: 'View page',
                                    route: '/save'
                                
                            ]
                        />
                        ) 
                    />
                </div>
            </BrowserRouter>
        )
    

【问题讨论】:

尝试使用withRouter 像这样的IconMenu 组件 - export default withRouter(IconMenu); 我刚试了下,还是报错。虽然它现在说: > 错误:不变失败:您不应该在 之外使用 此问题的其他可能原因 - ***.com/questions/55552147/… 感谢您提供此信息。我刚刚尝试了一些东西,这可能是一个“解决方案”,我稍后会发布它 【参考方案1】:

更新

这只是在避免这个问题。实际解决方案见Sunknudsen's answer

开始原始答案

所以,这不是最好的解决方案,并且确实避免了这个问题,但这就是我解决它的方法。

第 1 部分:NPM 包

IconMenu.js

import React from 'react';
import Icon from '@IDB/react-iconlibrary';

const IconMenu = ( items ) => 
    return (
        <div className='icon_menu'>
            items.map((item, index) => (
                item.render(<Icon className='icon_box_icon' key=index name=item.icon title=item.name />)
            ))
        </div>
    )

export default IconMenu 

第 2 部分:项目

Project.js

import  IconMenu  from '@IDB/ui-elements';
import React,  Component  from 'react';
import  BrowserRouter, Route, Link  from 'react-router-dom';

export default class Project extends Component  
    render () 
        return (
            <BrowserRouter>
                <div id='page'>
                    <Route path='/' render=(props) => (
                        <IconMenu 
                            items=[
                                
                                    icon: 'Edit',
                                    name: 'Edit page',
                                    render: (item) => <Link to='/test'>item</Link>
                                ,
                                
                                    icon: 'View',
                                    name: 'View page',
                                    render: (item) => <Link to='/test'>item</Link>
                                
                            ]
                        />
                        ) 
                    />
                </div>
            </BrowserRouter>
        )
    

【讨论】:

感谢分享塞德里克。您最终是否设法将 Link 包含在您的包中? @sunknudsen 不是;但我也进入了另一个项目,我们使用 Next.js 的静态页面进行路由。因此,上述问题将不再相关。但是,我不认为我的解决方案是“最终的”。如果其他人有更好的解决方案允许在外部包中使用 Link,那就太棒了。 嘿 Cédric,经过数小时的调试,我相信我找到了问题所在。请参阅下面的答案。【参考方案2】:

我有一个similar issue 并且能够使用以下方法解决它。

但首先,我们为什么会遇到这种极端情况?

react-router-dom 的两个实例存在且conflict 彼此存在时,将引发此错误。

在我的用例中,这发生在我使用 npm link“安装”我正在开发的一个 npm 包以抽象 Link 时。

我相信这个问题中描述的用例,也许 react-router-dom 是作为依赖项安装的,而它本应作为 peer dependency 安装(以减轻冲突)。

这是我package.json的摘录。


  "name": "react-hashlink",
  "peerDependencies": 
    "react": "^17.0.0",
    "react-dom": "^17.0.0",
    "react-router-dom": "^5.0.0"
  ,
  "dependencies": ,
  "devDependencies": 
    "@types/react": "^17.0.0",
    "@types/react-router-dom": "^5.1.7",
    "npm-check-updates": "^10.2.5",
    "typescript": "^4.1.3"
  

受 React docs 启发的解决方案

一种可能的解决方法是从mylib 运行npm link ../myapp/node_modules/react。这应该使库使用应用程序的 React 副本。

我创建了两个帮助脚本来链接和取消链接宿主项目的对等依赖项。

.env

PEER_DEPS_NODE_MODULES_PATH=/Users/sunknudsen/Code/sunknudsen/sunknudsen-website/node_modules

link-peer-deps.sh

#!/bin/sh

if [ -f .env ]
then
  export $(cat .env | sed 's/#.*//g' | xargs)
fi

npm link \
  $PEER_DEPS_NODE_MODULES_PATH/react \
  $PEER_DEPS_NODE_MODULES_PATH/react-dom \
  $PEER_DEPS_NODE_MODULES_PATH/react-router-dom

unlink-peer-deps.sh

#!/bin/sh

npm unlink react react-dom react-router-dom

希望这对其他人有所帮助!

【讨论】:

伟大的发现@sunknudsen!感谢分享。我确实希望这个问题将来可以帮助其他人。

以上是关于如何在外部 npm 包中使用 React 的 Link 组件而不会出现错误:不变的“你不应该在路由器外部使用链接”的主要内容,如果未能解决你的问题,请参考以下文章

为啥我无法在外部点击时使用 react js 访问我的两个容器的当前目标值?

(现代)React 功能组件可以在外部公开有状态方法吗?

如何在外部模块中使用 Laravel 类?

我自己的包中的npm包babel错误

使用 Bootstrap,如何在外部单击后关闭菜单?

将应用程序的一部分分解到它自己的 NPM 包中会导致更大的整体应用程序大小