Skip to content
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

[Bug]: Docs Rendering does not recover from errors thrown in React #21440

Closed
tmeasday opened this issue Mar 7, 2023 · 14 comments · Fixed by #21454
Closed

[Bug]: Docs Rendering does not recover from errors thrown in React #21440

tmeasday opened this issue Mar 7, 2023 · 14 comments · Fixed by #21454
Assignees

Comments

@tmeasday
Copy link
Member

tmeasday commented Mar 7, 2023

Describe the bug

If you get an error in your MDX that comes from React (e.g. invalid value passed to useOf), and you fix it on HMR, all you get is a white screen.

To Reproduce

  1. yarn start
  2. Add to Introduction.mdx:
import * as ButtonStories from './Button.stories';

<Meta of={ButtonStories} />
<Story of={ButtonStories} />
  1. You see an error:
    image

  2. Change to <Story of={ButtonStories.Primary} />

  3. You see a white screen, no error in console:

image

System

No response

Additional context

No response

@tmeasday tmeasday self-assigned this Mar 7, 2023
@shilman shilman moved this to Required for GA in Core Team Projects Mar 7, 2023
@tmeasday tmeasday moved this from Required for GA to In Progress in Core Team Projects Mar 7, 2023
@github-project-automation github-project-automation bot moved this from In Progress to Done in Core Team Projects Mar 7, 2023
@shilman
Copy link
Member

shilman commented Mar 8, 2023

Shiver me timbers!! I just released https://github.com/storybookjs/storybook/releases/tag/v7.0.0-beta.63 containing PR #21454 that references this issue. Upgrade today to the @next NPM tag to try it out!

npx sb@next upgrade --prerelease

@wintercounter
Copy link

Unfortunately, I'm still experiencing this issue using 7.0.6. Ever since I upgraded to v7 I constantly need to restart Storybook as it's not recovering after I make slight modifications to my components.

I'm using it with React+Webpack, CSF 3.

MDX:

import * as Stories from './button.stories';

<Meta title="Elements/Button" />
<Canvas of={Stories.Showcase} />

@tmeasday
Copy link
Member Author

@wintercounter are the errors in the .mdx files or the components? I think you might want to open another issue for this with a reproduction.

@yannbf
Copy link
Member

yannbf commented Apr 19, 2023

@tmeasday I found out how to reproduce it, it's quite simple actually, just add a throw new Error('bla') to a component

error.recovery.mov

@wintercounter
Copy link

@tmeasday It's happening in docs. It's not consistent to reproduce it for me. Sometimes it doesn't do it for a longer period, sometimes on the first change. The error I get is exactly the same as OP (different from @yannbf's repro). It's an even bigger issue that many times it does this on error-free changes, like duplicating a simple <div />.

Error with stack:

Error
Invalid value passed to the 'of' prop. The value was resolved to a 'component or unknown' type but the only types for this block are: story.
- Did you pass a component to the 'of' prop when the block only supports a story or a meta?
- ... or vice versa?
- Did you pass a story, CSF file or meta to the 'of' prop that is not indexed, ie. is not targeted by the 'stories' globs in the main configuration?
Call Stack
 DocsContext.resolveOf
  runtime.mjs:82:4027
 useSourceProps
  vendors-node_modules_storybook_addon-docs_dist_chunk-PCJTTTQV_mjs.iframe.bundle.js:468:534
 useDeprecatedPreviewProps
  vendors-node_modules_storybook_addon-docs_dist_chunk-PCJTTTQV_mjs.iframe.bundle.js:488:1271
 Canvas
  vendors-node_modules_storybook_addon-docs_dist_chunk-PCJTTTQV_mjs.iframe.bundle.js:488:2202
 renderWithHooks
  vendors-node_modules_pmmmwh_react-refresh-webpack-plugin_client_ErrorOverlayEntry_js-node_mod-7ceeba.iframe.bundle.js:180051:18
 mountIndeterminateComponent
  vendors-node_modules_pmmmwh_react-refresh-webpack-plugin_client_ErrorOverlayEntry_js-node_mod-7ceeba.iframe.bundle.js:183815:13
 beginWork
  vendors-node_modules_pmmmwh_react-refresh-webpack-plugin_client_ErrorOverlayEntry_js-node_mod-7ceeba.iframe.bundle.js:185328:16
 HTMLUnknownElement.callCallback
  vendors-node_modules_pmmmwh_react-refresh-webpack-plugin_client_ErrorOverlayEntry_js-node_mod-7ceeba.iframe.bundle.js:167910:14
 Object.invokeGuardedCallbackDev
  vendors-node_modules_pmmmwh_react-refresh-webpack-plugin_client_ErrorOverlayEntry_js-node_mod-7ceeba.iframe.bundle.js:167959:16
 invokeGuardedCallback
  vendors-node_modules_pmmmwh_react-refresh-webpack-plugin_client_ErrorOverlayEntry_js-node_mod-7ceeba.iframe.bundle.js:168023:31

@wintercounter
Copy link

I've put a breakpoint where the error is. Here are the values around there when throwing the exception.

image
image
image

@wintercounter
Copy link

wintercounter commented Apr 19, 2023

Here is the initial value for resolved when restarting Storybook. (Sorry for the spam.) It seems like type is changing from story to component upon saving and that's causing the issue.

image

private resolveModuleExport<TType extends ResolvedModuleExportType>(
moduleExportOrType: ModuleExport
): ResolvedModuleExportFromType<TType, TRenderer> {
type TResolvedExport = ResolvedModuleExportFromType<TType, TRenderer>;
const csfFile = this.exportsToCSFFile.get(moduleExportOrType);
if (csfFile) return { type: 'meta', csfFile } as TResolvedExport;
const story = this.exportToStory.get(moduleExportOrType);
if (story) return { type: 'story', story } as TResolvedExport;
// If the export isn't a module, default or story export, we assume it is a component
return { type: 'component', component: moduleExportOrType } as TResolvedExport;
}

It seems like in the exportToStory Map this export won't exist, so it'll return as a component. I guess it's completely valid to receive a new module reference, but why is it the same reference sometimes and completely different other times?!

@tmeasday
Copy link
Member Author

tmeasday commented Apr 19, 2023

@yannbf I guess that's a new issue that was never actually addressed by this PR or #22127 shall we open a new issue? Probably easy to fix by changing the showMain here to unset the error?

showMain: () => {},
showError: ({ title, description }: { title: string; description: string }) =>
setError(new Error(`${title} - ${description}`)),
showException: (err: Error) => setError(err),

@tmeasday
Copy link
Member Author

@wintercounter Can you log what's actually in moduleExportToType when the error occurs? Is it empty or does it contain "old" story references?

It seems very odd though, IIRC the code path is that the HMR triggers a recreation of the DocsContext and a re-rendering of the DocsContainer (containing your MDX, and ultimately the Story block) with that new context. So I am not sure how the the Story block could end up talking to the old context.

It's not when you make >1 change at the same time?

The error I get is exactly the same as OP

To be clear the error in the OP is a legitimate error, this issue was about SB not recovering from that error, if you made a mistake and passed the wrong thing to <Story of={}>. But we can debug further here if you can't get a repro.

@tmeasday
Copy link
Member Author

tmeasday commented Apr 19, 2023

Maybe it is an interaction with React Refresh somehow. I wonder what component it is trying to re-render?

What's also odd in your stack trace is:

  vendors-node_modules_pmmmwh_react-refresh-webpack-plugin_client_ErrorOverlayEntry_js-node_mod-7ceeba.iframe.bundle.js:180051:18

It's rendering webpack's error overlay that causes the resolveOf issue? Maybe I am reading that wrong, React stacktraces are hard to understand.

@wintercounter
Copy link

wintercounter commented Apr 19, 2023

It's there above (3rd picture): #21440 (comment)

It is not caused by Fast Refresh. I tried disabling it and still have the same problem. The issue I believe is that it was always expected that upon editing, Storybook gets the same module reference. I guess that's not the case and that's not handled.

If you want I open a new issue, I just thought to keep/re-open this maybe as it's the same error message, but it's a new issue indeed.

@tmeasday
Copy link
Member Author

It's there above (3rd picture): #21440 (comment)

Sorry, I was hoping to see the value of this.exportToStory at that breakpoint.

The issue I believe is that it was always expected that upon editing, Storybook gets the same module reference. I guess that's not the case and that's not handled.

But it doesn't always happen, right? I mean it's never happened to me before :)

@wintercounter
Copy link

wintercounter commented Apr 20, 2023

It's undefined, since it's a new module reference and the Map doesn't have that yet.

image

UPDATE:

Actually, the Map is completely empty.

image

Checked csfFiles in the constructor:

First load (working)
image

After it's broken, it becomes empty:
image

@tmeasday
Copy link
Member Author

tmeasday commented Apr 21, 2023

Hmm, so csfFiles is set in the prepare function via store.loadEntry:

const { entryExports, csfFiles = [] } = await this.store.loadEntry(this.id);
if (this.torndown) throw PREPARE_ABORTED;
this.csfFiles = csfFiles;
this.exports = entryExports;

@wintercounter I wonder if you can put a logpoint in this function to see what the entry looks like on HMR:

const entry = await this.storyIdToEntry(id);
const { importFn, storyIndex } = this;
if (!storyIndex || !importFn) throw new Error(`loadEntry called before initialization`);
const storyImports = entry.type === 'docs' ? entry.storiesImports : [];

We've had a user report an issue before where a docs entry somehow got turned into a story entry on HMR. I've no idea how that could happen but if that's happening here, I guess that'd lead to storyImports being []?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

4 participants