storybookjs / storybook

Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation
https://storybook.js.org
MIT License
84.8k stars 9.33k forks source link

[Bug]: `@storybook/addon-themes` `withThemeByClassName` causes `Storybook preview hooks can only be called inside decorators and story functions.` when used with `<Story/>` in a markup decorator #24625

Open vkolova opened 1 year ago

vkolova commented 1 year ago

Describe the bug

I get Storybook preview hooks can only be called inside decorators and story functions. error when combining @storybook/addon-themes withThemeByClassName when globally wrapping stories in markup.

Error: Storybook preview hooks can only be called inside decorators and story functions.
    at invalidHooksError (http://localhost:6006/sb-preview/runtime.js:7:20231)
    at useStoryContext (http://localhost:6006/sb-preview/runtime.js:9:2119)
    at useParameter (http://localhost:6006/sb-preview/runtime.js:9:2226)
    at useThemeParameters (http://localhost:6006/node_modules/.cache/sb-vite/deps/@storybook_addon-themes.js?v=cee3559b:28:46)
    at http://localhost:6006/node_modules/.cache/sb-vite/deps/@storybook_addon-themes.js?v=cee3559b:36:27
    at hookified (http://localhost:6006/sb-preview/runtime.js:7:18973)
    at http://localhost:6006/sb-preview/runtime.js:41:1451
    at http://localhost:6006/sb-preview/runtime.js:41:1912
    at renderWithHooks (http://localhost:6006/node_modules/.cache/sb-vite/deps/chunk-GEF4JVPU.js?v=cee3559b:11494:26)
    at updateFunctionComponent (http://localhost:6006/node_modules/.cache/sb-vite/deps/chunk-GEF4JVPU.js?v=cee3559b:13355:28)

It's a React Vite (TypeScript) project.

To Reproduce

Any component that will re-render on interaction works.

This combination works:

        withThemeByClassName({
          themes: {
            light: 'my-theme--light',
            dark: 'my-theme--dark',
          },
          defaultTheme: 'light',
        }),
        (Story) => {
          return <div>{Story()}</div>;
        }

But when Story is used as a component, as also show in the documation and how we've used it till we migrated to Storybook 7, it crashes:

        withThemeByClassName({
          themes: {
            light: 'my-theme--light',
            dark: 'my-theme--dark',
          },
          defaultTheme: 'light',
        }),
        (Story) => {
          return <div>{<Story/>}</div>;
        }

System

Storybook Environment Info:

  System:
    OS: Linux 6.2 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish)
    CPU: (8) x64 Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz
    Shell: 5.1.16 - /bin/bash
  Binaries:
    Node: 20.7.0 - ~/.nvm/versions/node/v20.7.0/bin/node
    npm: 10.1.0 - ~/.nvm/versions/node/v20.7.0/bin/npm <----- active
  Browsers:
    Chrome: 118.0.5993.117
  npmPackages:
    @storybook/addon-essentials: ^7.5.1 => 7.5.2 
    @storybook/addon-interactions: ^7.5.1 => 7.5.2 
    @storybook/addon-links: ^7.5.1 => 7.5.2 
    @storybook/addon-onboarding: ^1.0.8 => 1.0.8 
    @storybook/addon-themes: ^7.5.1 => 7.5.2 
    @storybook/blocks: ^7.5.1 => 7.5.2 
    @storybook/builder-vite: ^7.5.1 => 7.5.2 
    @storybook/cli: ^7.5.1 => 7.5.2 
    @storybook/react: ^7.5.1 => 7.5.2 
    @storybook/react-vite: ^7.5.1 => 7.5.2 
    @storybook/react-webpack5: ^7.5.1 => 7.5.2 
    @storybook/testing-library: ^0.2.2 => 0.2.2 
    chromatic: ^5.9.2 => 5.10.2 
    storybook: ^7.5.1 => 7.5.2

Additional context

I actually couldn't create a reproduction, because of error when transforming preview.ts (which probably indicates we shouldn't use JSX syntax?) We are using Babel in our project and everything works, up untill a component needs re-rendering. Am I missing something or is this expected behaviour?

TheAlexGo commented 1 year ago

same

valentinpalkovic commented 1 year ago

This issue is very well known. Unfortunately, I don't understand the main reason behind the problem. Using Story() instead of <Story /> is indeed our recommendation for everyone, who encounters this error. @shilman I wonder whether we should adjust the documentation to always use the Story() syntax since the JSX one is coupled to this annoying "hooks can only be called inside decorators" issue.

ShaunEvening commented 1 year ago

I think that makes the most sense, @valentinpalkovic as it also makes the pattern consistent across all view layers.

Arizona-dev commented 1 year ago

I have the same issue with withThemeByDataAttribute from @storybook/addon-themes.

ShaunEvening commented 11 months ago

Hey @Arizona-dev, were you able to solve it by using the story as a function instead of JSX?

Erns commented 11 months ago

I was having this same issue, but tried swapping the order of decorators and that appeared to help

   (Story) => {
      return <div>{<Story/>}</div>;
    },
    withThemeByClassName({
      themes: {
        light: 'my-theme--light',
        dark: 'my-theme--dark',
      },
      defaultTheme: 'light',
    })
ShaunEvening commented 11 months ago

@Erns, can you try changing your custom decorator to return <div>{Story()}</div> instead of using Story as a JSX tag?

SalahAdDin commented 9 months ago

@Erns, can you try changing your custom decorator to return <div>{Story()}</div> instead of using Story as a JSX tag?

It does not work when working with RSC.

yannbf commented 6 months ago

Here's a simple, minimal reproduction for this issue with Addon themes. The funny thing is that it does not happen with React Vite, but it happens with React + Nextjs.

https://github.com/yannbf/themes-hooks-issue-repro

If useCustomHook is called inside of the render function, it breaks. If it's called as part of <Component />, then it works.

const useCustomHook = () => {
  const [value, setValue] = useState<number>()

  useEffect(() => {
    setValue(123)
  }, [])

  return value
}

const Component = () => {
  const value = useCustomHook()
  return <div>Hello world {value}</div>
}

export const Primary: Story = {
  render: () => {
    // if you uncomment the line below, the story will break with:
    // Error: Storybook preview hooks can only be called inside decorators and story functions.
    // useCustomHook()
    return <Component />
  },
}

@valentinpalkovic any idea? could it be related to SWC for some reason, given that it won't happen in React Vite?

cc @italoteix

boniSantana commented 4 months ago

I have a similar error, all my stories are okay but when I try to call a async component it breaks, but if I remove 'withThemeByClassName' works fine.

Minimal reproduction

import type { Meta, StoryObj } from '@storybook/react'

async function TestAsyncComponentExample(){
    return <div> Holis</div>
}

const meta: Meta<typeof TestAsyncComponentExample> = {
    title: 'Components/PressureLoadsPage',
    component: TestAsyncComponentExample,

}

export default meta
type Story = StoryObj<typeof TestAsyncComponentExample>;

export const Default: Story = {
    args: {},

}
lqhuang commented 3 months ago

I have a similar error, all my stories are okay but when I try to call a async component it breaks, but if I remove 'withThemeByClassName' works fine.

Minimal reproduction

import type { Meta, StoryObj } from '@storybook/react'

async function TestAsyncComponentExample(){
    return <div> Holis</div>
}

const meta: Meta<typeof TestAsyncComponentExample> = {
  title: 'Components/PressureLoadsPage',
  component: TestAsyncComponentExample,

}

export default meta
type Story = StoryObj<typeof TestAsyncComponentExample>;

export const Default: Story = {
  args: {},

}

Same issue with "async Component"

chris-erickson commented 1 month ago

@shilman or @ShaunEvening can anyone boost this up? With RSC becoming a very common way to build NextJS apps now, this bug is starting to drive design decisions, when none of the suggested temp solutions seem to work. (Changing all decorators to be a function call, or moving withThemeByClassName to the end of the list of global decorators in preview.tsx). Having to make a choice to either not use RSC, or not use Storybook for any RSC components if we want theming support.. 😔

https://github.com/storybookjs/storybook/issues/24625 https://github.com/storybookjs/storybook/issues/26239 https://github.com/storybookjs/storybook/issues/22132 https://github.com/storybookjs/storybook/pull/26243

shilman commented 1 month ago

@chris-erickson can you do me a favor and try out our next vite based next framework

https://storybook.js.org/docs/get-started/frameworks/nextjs#with-vite

According to @yannbf 's comment above theming works in Vite. And while the new package is currently experimental it is way faster than the webpack framework and also compatible with all the next-gen testing stuff we're working on.

If it works it would be one more reason for us to elevate that to our recommended framework for Next.

flash42 commented 3 weeks ago

@shilman I think @yannbf was referring to react-vite framework NOT the nextjs-vite framework. I've tried it with nextjs-vite and still if I initialised a component in render the hooks error was thrown. The workaround for me was in this comment - extracting the render function as a separate component fixes the issue.

shilman commented 3 weeks ago

@flash42 The experimental-nextjs-vite framework didn't exist when @yannbf wrote the comment. It provides routing support, image component, etc like the nextjs framework, but using the vite builder. If you're building a nextjs app but want to use vite, it's the way to go.

GerroDen commented 2 weeks ago

Hi, I'm facing the same issue with normal react components and also Story() or <Story/> doesn't matter. I can only confirm, that this was in issue in the past, we worked somehow around it and it now reappeared with Storybook 8.4