You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 21:13:29