使用Storybook测试组件时next/navigation的useParams()返回null求助
在Storybook中使用Next.js useParams()返回null的解决办法
Next.js的useParams()依赖其自身的路由运行时环境,而Storybook是独立的组件测试环境,默认不会提供Next的路由上下文,所以调用该钩子时会返回null。下面是几种可行的解决方法:
方法1:Mock useParams钩子
直接在Storybook中mockuseParams,返回测试所需的参数。可以全局配置或针对单个Story设置:
全局Mock(preview.ts中配置)
import { Preview } from '@storybook/react'; import '../dist/tailwind.css'; import { useParams } from 'next/navigation'; // 全局mock useParams,所有Story都会使用这个返回值 jest.mock('next/navigation', () => ({ ...jest.requireActual('next/navigation'), useParams: () => ({ locale: 'en', currency: 'USD', ccode: 'US', slug: 'product-1', category_slug: 'electronics', }), })); const preview: Preview = { parameters: {}, }; export default preview;
单个Story中Mock
import { Breadcrumbs } from './Breadcrumbs'; import { useParams } from 'next/navigation'; jest.mock('next/navigation'); export default { component: Breadcrumbs, title: 'Components/Breadcrumbs', }; const Template = (args) => <Breadcrumbs {...args} />; export const Default = Template.bind({}); Default.play = () => { // 为当前Story设置自定义的参数返回值 (useParams as jest.Mock).mockReturnValue({ locale: 'zh-CN', currency: 'CNY', ccode: 'CN', slug: 'product-2', category_slug: 'home-goods', }); };
方法2:手动提供Next路由上下文
通过Next的路由上下文组件包裹测试组件,模拟真实路由环境:
import { Breadcrumbs } from './Breadcrumbs'; import { NextRouterContext } from 'next/dist/shared/lib/router-context'; export default { component: Breadcrumbs, title: 'Components/Breadcrumbs', }; const Template = (args) => ( <NextRouterContext.Provider value={{ asPath: '/zh-CN/CN/CNY/home-goods/product-2', pathname: '/[locale]/[ccode]/[currency]/[category_slug]/[slug]', query: { locale: 'zh-CN', currency: 'CNY', ccode: 'CN', slug: 'product-2', category_slug: 'home-goods', }, // 按需补充其他router必要属性,用jest.fn()模拟方法 push: jest.fn(), replace: jest.fn(), prefetch: jest.fn(), }} > <Breadcrumbs {...args} /> </NextRouterContext.Provider> ); export const Default = Template.bind({}); Default.args = { breadcrumb: [], // 根据你的组件props传入对应值 };
方法3:重构组件,将参数作为Props传入(推荐)
让组件摆脱对useParams的依赖,直接通过props接收所需参数,既提高组件复用性,也让测试更简单:
修改Breadcrumbs组件
// 更新组件props定义,添加所需参数 interface BreadcrumbsPropsTypes { breadcrumb: any[]; // 保持原有props locale: string; ccode: string; } const Breadcrumbs: FC<BreadcrumbsPropsTypes> = ({ breadcrumb, locale, ccode }) => { // 直接使用传入的locale和ccode,无需调用useGmpParams // ... 组件原有逻辑 };
在Story中传参
import { Breadcrumbs } from './Breadcrumbs'; export default { component: Breadcrumbs, title: 'Components/Breadcrumbs', }; const Template = (args) => <Breadcrumbs {...args} />; export const Default = Template.bind({}); Default.args = { breadcrumb: [], locale: 'en', ccode: 'US', }; export const ChineseLocale = Template.bind({}); ChineseLocale.args = { breadcrumb: [], locale: 'zh-CN', ccode: 'CN', };
方法对比
- Mock钩子:快速实现,无需修改组件,但全局mock会影响所有Story,适合临时测试。
- 路由上下文模拟:更贴近真实运行环境,但需要配置较多路由属性,适合需要完整路由场景的测试。
- Props传参:组件解耦,测试灵活,是长期维护的最佳实践,推荐优先采用。
内容的提问来源于stack exchange,提问作者Kanaiya kamboj
相关产品推荐
相关产品推荐

