Storybook w/ react-router - 你不应该在 <Router> 之外使用 <Link>

Storybook w/ react-router - You should not use <Link> outside <Router>

发布问题的解决方案我很难找到,尽管 Sensei 谷歌搜索技能

尽管我的带有 react-router 的应用程序没有任何问题 Storybook 抛出错误“不变失败:你不应该在外部使用”。

Error: Invariant failed: You should not use <Link> outside a <Router>
    at invariant (tiny-invariant.esm.js:11)
    at react-router-dom.js:181
    at updateContextConsumer (react-dom.development.js:19747)
    at beginWork (react-dom.development.js:20079)
    at HTMLUnknownElement.callCallback (react-dom.development.js:358)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:410)
    at invokeGuardedCallback (react-dom.development.js:463)
    at beginWork$ (react-dom.development.js:25730)
    at performUnitOfWork (react-dom.development.js:24631)
    at workLoopSync (react-dom.development.js:24613)

奇怪,因为该应用程序有效(因此没有在外面使用)。

以下是对我有用的方法:

  1. 在您的 .storybook 文件夹中创建一个 preview.js 文件。
  2. 在您的 preview.js 文件中添加以下全局装饰器。
    import React from "react";
    import { addDecorator } from "@storybook/react";
    import { MemoryRouter } from "react-router";
    
    addDecorator(story => <MemoryRouter initialEntries={['/']}>{story()}</MemoryRouter>);

备注

  • 使用的故事书版本:"@storybook/react": "^5.3.13"
  • 基于此解决方案here
  • 关于全局装饰器的更多信息here

问题是 Storybook 渲染了各个故事。在这种情况下,使用 <Link> 的组件实际上是在 <Router>.

之外呈现的

解决方案是使用 addDecorator 用 <Router> 包装单个故事;

//config.js
//...
addDecorator(story => <Router>{story()}</Router>);

这对我有用。在装饰器内添加 Memory Router 属性.

import React from 'react';
import {MemoryRouter} from 'react-router-dom';
import //your component// from //component location//

export default {
title : //How you want your component to show in storybook eg: 'Components/Button'
component : //Specify the name of component that you imported eg: 'Button'
decorators : [(Story) => (<MemoryRouter><Story/></MemoryRouter>)] //Wrapping the story inside the router
};

对于 Storybook 6.1 版,storybook-router 采用新的代码表示法。这是单个组件的示例:

import React from 'react';
import StoryRouter from 'storybook-react-router';
import //your component// from //component location//

export default {
  title: '//your component//',
  component: //your component//,
  decorators: [StoryRouter()],
};

export const Name = () => <//your component// />;

进一步了解 Web-ski 的回答。将 StoryRouter 添加到您的 .storybook/preview.js

import StoryRouter from 'storybook-react-router';

addDecorator(StoryRouter());

它现在可以在全球范围内的每个故事中使用。

如果您想对单个组件而不是全局执行此操作,则在故事文件中,将组件包装在内存路由器中。

例如,我有一个 header 组件,它有一个 Link 元素,所以在我的 header.stories.tsx 文件中,我将更改以下内容:

const Template: ComponentStory<typeof Header> = (args) => (
    <Header {...args} />
);

const Template: ComponentStory<typeof Header> = (args) => (
  <MemoryRouter>
    <Header {...args} />
  </MemoryRouter>
);