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

Core: Don't recreate a bound story function each time we call a decorated story #14692

Merged
merged 7 commits into from
Apr 27, 2021

Conversation

tmeasday
Copy link
Member

Issue:#12255

What I did

Rather than using a closure and rebinding the story function with the context as it is decorated, instead use a getStoryContext() function which is defined once at decoration time to supply the context.

How to test

  • Is this testable with Jest or Chromatic screenshots?

We have a Chromatic test. Will think about a Jest one

  • Does this need a new example in the kitchen sink apps?

There is one

  • Does this need an update to the documentation?

No

lib/addons/src/types.ts Outdated Show resolved Hide resolved
@tmeasday tmeasday force-pushed the 12255-fix-jsx-decorators-4 branch from e37fb51 to 3934b7c Compare April 23, 2021 03:58
@tmeasday tmeasday requested a review from ndelangen April 23, 2021 04:12
@tmeasday
Copy link
Member Author

@ndelangen I figured out some tests and it's passing 🎉

Here's the typing change: #14701

Comment on lines +45 to +64
export const defaultDecorateStory = (
storyFn: LegacyStoryFn,
decorators: DecoratorFunction[]
): LegacyStoryFn => {
// We use a trick to avoid recreating the bound story function inside `decorateStory`.
// Instead we pass it a context "getter", which is defined once (at "decoration time")
// The getter reads a variable which is scoped to this call of `decorateStory`
// (ie to this story), so there is no possibility of overlap.
// This will break if you call the same story twice interleaved
// (React might do it if you rendered the same story twice in the one ReactDom.render call, for instance)
let contextStore: StoryContext;
const decoratedWithContextStore = decorators.reduce(
(story, decorator) => decorateStory(story, decorator, () => contextStore),
storyFn
);
return (context = defaultContext) => {
contextStore = context;
return decoratedWithContextStore(context); // Pass the context directly into the first decorator
};
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to make changes to the other decorateStory functions (vue has one, and I think angular has one too)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or is that simply not needed since the problem we're solving is react-specific?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not creating a new function is a general performance optimization though I think.. Should use less memory and fewer garbage collections needed.

Unless I'm wrong about that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you are right. It does make the decorateStory function a little bit more complex and hard to reason about (perhaps that's debatable?) and the angular/vue/etc versions are sort of complicated already.

I'm open to changing it if you want, I'm not sure the perf difference is really going to be a big deal (stories don't get rendered all that often).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer making all decorateStory function be act similar, so that when you understand 1, you understand them all.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed this with @shilman.

We aren't super comfortable changing the already quite complex decorator "applicators" for other view layers. We don't understand the view layers well enough to know if it is a good idea or will change the behaviour in unexpected ways.

I think this will serve as a template for future view layers and if maintainers of other view layers want to bring it across as an optimization/simplification they can.

Copy link
Member

@shilman shilman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 💯

@shilman shilman changed the title Avoid recreating a bound story function each time we call a decorated story Core: Don't recreate a bound story function each time we call a decorated story Apr 27, 2021
@shilman shilman merged commit 1c4e048 into next Apr 27, 2021
@shilman shilman deleted the 12255-fix-jsx-decorators-4 branch April 27, 2021 07:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants