-
-
Notifications
You must be signed in to change notification settings - Fork 9.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
preview > decorators causes: "Rendered more hooks than during the previous render." #15223
Comments
Seems to happen for me when React Refresh is enabled. |
Can you please create a reproduction by running |
@shilman It should be the same problem as in storybookjs/addon-knobs#2 - reacts StrictMode. Had the same issue with storybook-addon-next-router. @strothj @pixelass Could you check and confirm if you had reactOptions.strictMode = true in main.js? |
I definitely did not set |
I've also the similar issue and only way I could get rid of it, was removing my exported decorators in |
I just enabled Seems to come from a combination of Will try to make repro. |
We just also run in that issue. @shilman here is a reproduction-repo based on sb@next init https://github.com/seppzero/sb-decorator-issue. |
We just ran into this issue upgrading from storybook 6.3 to 6.4. One thing I noticed when stepping through the debugger: In the jsxDecorator:
Our story triggers Suspense due to a third-party library we use, so StoryFn() throws a promise the first time, which I believe will make that However, looking at the reproduction repo that was made, it seems to be failing in withOutline instead, and I don't see any problematic code there that would conditionally execute a hook, so it seems like there might be something more fundamental going on? |
After more investigation, I think there are 2 separate issues here. I will open a new issue for the issue with jsxDecorator and Suspense, it seems unrelated to the original issue with strictMode and preview decorators, as I can reproduce my issue regardless of the strictMode or decorators status. For the original issue here, I added some logging to the addons/dist/esm/hooks.js when running the repro. I don't completely understand the entire flow with the hooks, but here is what I observed with the original issue here. I created a new repro with the plain CRA template, then just add the decorator to preview.js and enable strict mode and then the hooks error shows up on all the stories. With no strict mode and no preview decorator, the useHook gets called several times in MOUNT, but no UPDATE and it works |
I encountered this error after enabling strict mode and fast refresh, and was able to resolve it in my decorator function by changing i.e. // preview.tsx ❌ errors
import {DecoratorFn} from '@storybook/react';
export const decorators: DecoratorFn[] = [
Story => {
return (
<ThemeProvider>
<Story />
</ThemeProvider>
);
},
]; // preview.tsx ✅ works
import {DecoratorFn} from '@storybook/react';
export const decorators: DecoratorFn[] = [
story => {
return (
<ThemeProvider>
{story()}
</ThemeProvider>
);
},
]; |
We just ran into this again, only this time it was caused by running storybook through Cypress tests. We have a decorator for our stories that need authentication to prompt to authenticate, and that decorator works fine in storybook alone, but as soon as we try to hit those stories with Cypress tests, then we get the hooks error and those stories fail. So that's a new wrinkle. I will continue to investigate and see if I can figure out why running inside the Cypress runner iframe causes the rendering and hooks to behave differently when just running normally. Switching our decorator from |
I am still facing the same issue. is there another workaround to try? I just commented out |
I have the same issue. Moving Another option was configuring
but that's not an option for me. |
FWIW I'm on v7, I have a stack of decorators and I had a lot of issues, especially Storybook's Story => <Whatever><Story /></Whatever> to (storyFn, context) => <Whatever>{storyFn(context)}</Whatever> and it seems to have solved the problems for me. |
I believe I have discovered another detail in the root cause. I discovered that our decorator conditionally renders the story function, as it waits for the user to be logged in before rendering the story. So the first few times it renders with a message that the story requires auth and a sign in button, then once they are signed in, it renders the story. This is what seems to cause the "Rendered more hooks". If I always render the story function it works fine, if I never render the story function it works fine, but if I render a time or two without the story function, then render the story function, the code in storybook that handles the hook context doesn't like that, because suddenly when the story function renders a lot more hooks are getting called than when the sign in button is shown. |
In my example, I want to render Story component with some delay:
So, don't I have a way to render a Story conditionally? |
@alex-knyazev In my setup I also wanted to render a story conditionally. I ended up doing the following as a workaround: export const decorators = [
( Story, context ) => {
...
if ( contentIsLoading ) {
return (
<p>loading</p>
<div hidden>
<Story />
</div>
);
}
return (
<Story />
);
},
]; |
This is how i do it: export const MyDecorator = (story: () => unknown) => {
const [loaded, setLoaded] = React.useState<boolean>();
React.useEffect(() => {
setLoaded(true);
}, []);
const Story = (
<Wrapper>
{story()}
</Wrapper>
);
if (!loaded) return <div>loading ...</div>;
return Story;
}; |
I have reproduced this issue today. https://github.com/marybeshaw/storybook-issue-15223 @seppzero reproduced this issue a year ago: https://github.com/seppzero/sb-decorator-issue. @shilman is there anything else you need to take off the "needs reproduction" tag? |
In my repro, the stackblitz link is wrong; it's the original version of preview.js, not the one I changed. |
We got the same issue. According to our internal Also, we precised we have the issue only by going to the dedicated "Docs" section of our stories. Classic story rendering works as expected. Our decorator is configured like so: // .storybook/preview.ts
export const decorators: DecoratorFn[] = [
StoryDecorator,
]; Our decorator: import React, {
FC,
ReactElement,
Suspense,
useEffect,
} from 'react';
import {
DecoratorFn,
} from '@storybook/react';
import {
themes,
} from '@storybook/theming';
import {
useDarkMode,
} from 'storybook-dark-mode';
import {
memoryStore,
useLocaleState,
} from 'react-admin';
import AppContext from '../../src/AppContext';
import {
fakeDataProvider,
} from '../../src/providers';
type AppDecoratorProps = {
locale: string;
children: ReactElement;
}
const AppDecorator: FC<AppDecoratorProps> = ({
locale,
children,
}) => {
const [_, setLocale] = useLocaleState();
useEffect(() => {
setLocale(locale);
}, [locale])
return children
}
export const StoryDecorator: DecoratorFn = (Story: any, {
globals: {
locale,
},
parameters,
}) => {
const dark = useDarkMode();
// @see https://github.com/hipstersmoothie/storybook-dark-mode/issues/235
useEffect(() => {
const backgroundColor = dark ? themes.dark.appBg : themes.light.appBg;
document.body.style.backgroundColor = backgroundColor || 'inherit';
}, [dark]);
return (
<React.StrictMode>
<Suspense fallback="loading...">
<AppContext
memoryRouter={parameters.memoryRouter}
darkMode={dark}
dataProvider={fakeDataProvider()}
store={memoryStore()}
>
<AppDecorator locale={locale}>
<Story />
</AppDecorator>
</AppContext>
</Suspense>
</React.StrictMode>
);
};
export default null; |
My team has reproduced this issue with Storybook 6.3 and also Storybook 7, so maybe there are different causes? |
I'm trying to give it a stab at fixing this issue as it is blocking a lot of our storybook work. After putting log messages where I think it's useful, I think I have some kind of rudimentary understanding of what is going on, but the lifecycle around this is a bit involved so I'm still not clear about everything that is going on.
does not fix the unit test like I thought it would, but it might be because of how the unit tests work. They have this special run function to simulate running the decorators multiple times, but when I log out what is happening it doesn't follow the same behavior that a story logs when it renders in the repros, so I'm thinking the unit tests run slightly differently than the actual code, and I don't understand this lifecycle well enough to figure out how. |
Looks like I'm somewhat mistaken. The internal decorators use custom look-alike hooks, so Storybook is not intercepting the hooks from user-provided decorators and stories that use React hooks, but preview-api has it's own custom React-like hooks that the built-in decorators use. The problem appears to be with how the jsxDecorator works. No errors, but also no UPDATE, only MOUNT. However, once our custom decorator is added, the biggest change is now the story has to go through the update phase, and the jsxDecorator gets out of sync. It never gets MOUNT called, but it gets UPDATE called twice at the end, and now we get the "Rendered more hooks" error: jsxDecorator uses 1 hook, |
Fixes storybookjs#15223 Custom preview-api hooks assumed that all decorators were always rendered however if a custom decorator conditionally rendered it's children, then not all decorators would get rendered, especially jsxDecorator. This would result in the error "Rendered more hooks than during the previous render." This removes the assumption that all decorators render every time and relies on each decorator to register itself during MOUNT phase which is handled when each decorator goes through `hookify`
I missed one spot where it was setting |
For those wondering why this issue may have started in 7.0.3 -- we moved the JSX decorator to the end of the list in #21907 So I suppose if there was a second "conditional" decorator that previously came after the JSX decorator, it now comes before, leading to this bug. But the issue can happen if you have conditional decorators no matter what, I suppose, it just depends on exactly what decorators you have. |
For us the issue started on |
Fixes storybookjs#15223 Custom preview-api hooks assumed that all decorators were always rendered however if a custom decorator conditionally rendered it's children, then not all decorators would get rendered, especially jsxDecorator. This would result in the error "Rendered more hooks than during the previous render." This removes the assumption that all decorators render every time and relies on each decorator to register itself during MOUNT phase which is handled when each decorator goes through `hookify`
I am getting this error on stories that have decorators (not conditional). Changing the syntax from Using Storybook 7.0.18 with Vite and React. |
|
Hey, the workarounds listed here don't work. |
Seeing the same problem now on 7.4.4 -> Had no problems on 7.4.2 before updating |
I had this issue because I was creating a new React component to satisfy my eslink react hook rules like this: import type { Meta, StoryObj } from "@storybook/react"
import { CName, CNameProps } from './cname'
export default { component: CName } satisfies Meta<typeof CName>
type Story = StoryObj<typeof CName>
function Wrapper() {
const [args, updateArgs] = useArgs<CNameProps>()
// ...
return <CName {/* ... */} >
}
export const storyName: Story = {
// ...
render: (args) => <Wrapper {...args} />
// ...
} But then I just changed it to a normal storybook style and add import type { Meta, StoryObj } from "@storybook/react"
import { CName, CNameProps } from './cname'
export default { component: CName } satisfies Meta<typeof CName>
type Story = StoryObj<typeof CName>
export const storyName: Story = {
// ...
render() {
// eslint-disable-next-line react-hooks/rules-of-hooks
const [args, updateArgs] = useArgs<CNameProps>()
// ...
return <CName {/* ... */} >
}
// ...
} Hopefully this can help someone 😄 |
seeing the same problem on 8.0.10. Also changing |
Describe the bug
When adding a decorator to
.storybook/preview.js
an error occurs: "Rendered more hooks than during the previous render."To Reproduce
Repro: TBD
Add the following code to your
.storybook/preview.js
System
The text was updated successfully, but these errors were encountered: