发布了一个问题的解决方案,尽管我很难找到Sensei的谷歌搜索技巧。
尽管我的应用程序与反应路由器工作没有任何问题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$1 (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$$1 (react-dom.development.js:25730)
at performUnitOfWork (react-dom.development.js:24631)
at workLoopSync (react-dom.development.js:24613)
奇怪的是,该应用程序工作正常(因此,在<路由器>之外没有使用<链接>(。
以下是对我有效的方法:
- 在
.storybook
文件夹中创建一个preview.js
文件 - 在
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"
- 基于此解决方案
- 点击此处了解更多关于全球装饰师的信息
编辑:Storybook V7更新(2022年12月(
将
preview.js
重命名为preview.tsx
(我想它与jsx的工作原理相同,但我的项目是用TypeScript制作的(addDecorator
函数不再适用于v7,所以您需要像这样添加它。
import React from "react";
import { MemoryRouter } from "react-router";
export const decorators = [
(Story) => (
<MemoryRouter initialEntries={['/']}>
<Story />
</MemoryRouter>
),
];
这对我很有用。在decorators属性中添加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
};
对于6.1版本的Storybook,在新的代码表示法中使用Storybook路由器。以下是单个组件的示例:
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滑雪的答案。将StoryRouter添加到.storybook/preview.js
import StoryRouter from 'storybook-react-router';
addDecorator(StoryRouter());
现在,它在全球范围内,在每个故事中都可以使用。
如果您想对单个组件而不是全局执行此操作,那么在故事文件中,将组件封装在内存路由器中。
例如,我有一个标头组件,它有一个Link元素,所以在我的header.stores.tsx文件中,我会更改以下内容:
const Template: ComponentStory<typeof Header> = (args) => (
<Header {...args} />
);
至
const Template: ComponentStory<typeof Header> = (args) => (
<MemoryRouter>
<Header {...args} />
</MemoryRouter>
);
Fabian的答案是目前得票最多的答案,只有当你不想在你的故事中使用React Router的数据API时,它才能解决问题。
这是我对Fabian解决方案的修改版本,增加了使用React Router数据API的能力,因此可以使用数据路由器特定的功能,如扩展的Form
组件:
import { createMemoryRouter, RouterProvider } from "react-router-dom";
import { addDecorator } from "@storybook/react";
addDecorator((story) => {
const router = createMemoryRouter([{ path: "/", element: story() }], {
initialEntries: ["/"],
});
return <RouterProvider router={router} />;
});
问题是Storybook渲染单个故事。在这种情况下,使用<Link>
的组件实际上是在<Router>
之外呈现的。
解决方案是使用addDecorator用<Router>
包装单个故事;
//config.js
//...
addDecorator(story => <Router>{story()}</Router>);
如果您还想使用路由路径参数,则需要使用故事书装饰器。用户Web-ski和sidonaldson已经展示了如何将路由器mock添加到故事书中。
以下是我在react v 18 中实现的故事书路由器mock
预览.js文件
import { withRouter } from "storybook-addon-react-router-v6";
export const decorators = [withRouter];
component.story.js文件
import Component from "./component";
export default {
component: Component,
title: "component with rout path and parameters",
parameters: {
reactRouter: {
routePath: "/root/:primary/:secondary",
routeParams: { primary: "firstparam", secondary: "secondparam" },
},
},
};
const Template = (args) => <Component {...args} />;
export const Default = Template.bind({});
Default.args = {
// ommited
};
这将为useParams等函数提供以下结果:
const {primary, secondary} = useParams();
console.log('primary value: ' + primary)
console.log('secondary value: ' + secondary)
// outputs: primary value: firstparam
// outputs: secondary value: secondparam