source

리액션 라우터 포함 스토리북 - 외부 사용 금지

bestscript 2023. 3. 6. 21:14

리액션 라우터 포함 스토리북 - 외부 사용 금지

문제의 해결책을 게시하는 것은 선생님 구글링 기술이지만 찾기가 어려웠습니다.

리액트 라우터를 탑재한 앱은 문제없이 작동했지만 Storybook은 "Invariant failed:<Router> 외부에서 <Link>를 사용하지 마십시오.

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)

이상하게도 앱이 작동하기 때문에 <Router> 이외에서는 <Link>가 사용되지 않았습니다.

다음과 같은 이점이 있습니다.

  1. 작성하다preview.js파일을 제출하다.storybook폴더입니다.
  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"
  • 솔루션에 근거하고 있습니다.
  • 글로벌 데코레이터에 대한 자세한 내용은 이쪽

편집 : 스토리북 V7 업데이트(2022년 12월)

  1. 이름 바꾸기preview.js로.preview.tsx(jsx에서도 동일하게 동작한다고 생각합니다만, 제 프로젝트는 TypeScript로 되어 있습니다.)

  2. addDecoratorv7에서는 기능을 사용할 수 없기 때문에 이렇게 추가해야 합니다.

import React from "react";
import { MemoryRouter } from "react-router";
    
export const decorators = [
  (Story) => (
    <MemoryRouter initialEntries={['/']}>
      <Story />
    </MemoryRouter>
  ),
];

이게 나한테 효과가 있었어.데코레이터 속성 내에 메모리 라우터를 추가합니다.

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에서는 새로운 코드 표기법으로 스토리북 라우터가 동작합니다.단일 컴포넌트의 예를 다음에 나타냅니다.

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());

현재는 전 세계에서 모든 스토리에서 이용할 수 있습니다.

글로벌이 아닌 개별 컴포넌트에 대해 이 작업을 수행하려면 스토리 파일에서 컴포넌트를 메모리라우터로 랩합니다.

예를 들어 헤더 컴포넌트에 Link 요소가 포함되어 있기 때문에 header.stories.tsx 파일에서 다음을 변경합니다.

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

로.

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

현재 가장 많은 표를 얻고 있는 Fabian의 답변은 React Router의 데이터 API를 사용하고 싶지 않은 경우에만 문제를 해결합니다.

다음은 리액트 라우터의 데이터 API를 사용할 수 있는 기능을 추가한 Fabian의 수정판 회피책입니다.이를 통해 확장 컴포넌트와 같은 데이터 라우터 고유의 기능을 사용할 수 있습니다.

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>.

해결책은 개별 스토리를 정리하는 것이었습니다.<Router>addDecorator 사용;

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

루트 패스 파라미터도 사용하려면 스토리북 데코레이터를 사용해야 합니다.사용자 Web-ski와 sidonaldson은 이미 스토리북에 라우터 모크를 추가하는 방법을 보여 주었다.

리액트 v18에서의 스토리북 라우터 모의 구현입니다.

preview.preview 파일

import { withRouter } from "storybook-addon-react-router-v6";

export const decorators = [withRouter];

component.story.component 파일

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

언급URL : https://stackoverflow.com/questions/58909666/storybook-w-react-router-you-should-not-use-link-outside-router