Skip to content

Commit

Permalink
fixes storybookjs#17839 - handle suspense in story function
Browse files Browse the repository at this point in the history
If a story function triggers Suspense by throwing a promise liike
some third party hooks do, the story will fail to render
because the useEffect is skipped the first time and then
run the second time, resulting in the
"Rendered more hooks than during the previous render." error
This runs the story function after the useEffect hook so the same number of hooks
are run whether it triggers suspense or not, and in keeping
with the guidelines on not conditionally running hooks
  • Loading branch information
redbugz committed Apr 8, 2022
1 parent 75111ea commit 9c7ce59
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 1 deletion.
29 changes: 29 additions & 0 deletions app/react/src/client/docs/jsxDecorator.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -284,4 +284,33 @@ describe('jsxDecorator', () => {
'<div className="foo" />'
);
});

it('handles stories that trigger Suspense', async () => {
// if a story function uses a hook or other library that triggers suspense, it will throw a Promise until it is resolved
// and then it will return the story content after the promise is resolved
const storyFn = jest.fn();
storyFn
.mockImplementationOnce(() => {
throw Promise.resolve();
})
.mockImplementation(() => {
return <div>resolved args story</div>;
});
const jsx = '';
const context = makeContext('args', { __isArgsStory: true, jsx }, {});
expect(() => {
jsxDecorator(storyFn, context);
}).toThrow(Promise);
jsxDecorator(storyFn, context);
await new Promise((r) => setTimeout(r, 0));

expect(mockChannel.emit).toHaveBeenCalledTimes(2);
expect(mockChannel.emit).nthCalledWith(1, SNIPPET_RENDERED, 'jsx-test--args', '');
expect(mockChannel.emit).nthCalledWith(
2,
SNIPPET_RENDERED,
'jsx-test--args',
'<div>\n resolved args story\n</div>'
);
});
});
2 changes: 1 addition & 1 deletion app/react/src/client/docs/jsxDecorator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,14 @@ export const jsxDecorator = (
) => {
const channel = addons.getChannel();
const skip = skipJsxRender(context);
const story = storyFn();

let jsx = '';

useEffect(() => {
if (!skip) channel.emit(SNIPPET_RENDERED, (context || {}).id, jsx);
});

const story = storyFn();
// We only need to render JSX if the source block is actually going to
// consume it. Otherwise it's just slowing us down.
if (skip) {
Expand Down

0 comments on commit 9c7ce59

Please sign in to comment.