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

Always treat _createMdxContent as a JSX component #2445

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

remcohaszing
Copy link
Member

Initial checklist

  • I read the support docs
  • I read the contributing guide
  • I agree to follow the code of conduct
  • I searched issues and couldn’t find anything (or linked relevant results below)
  • If applicable, I’ve added docs and tests

Description of changes

If the user provides a wrapper component, _createMdxContent was treated as a JSX component. Otherwise it was invoked as a function. Because _createMdxContent uses a hook, this hooks becomes part of the MDXContent component conditionally. This breaks React’s rule of hooks.

Closes #2444

If the user provides a `wrapper` component, `_createMdxContent` was
treated as a JSX component. Otherwise it was invoked as a function.
Because `_createMdxContent` uses a hook, this hooks becomes part of the
`MDXContent` component conditionally. This breaks React’s rule of hooks.

Closes #2444
@remcohaszing remcohaszing added 🐛 type/bug This is a problem 🗄 area/interface This affects the public interface 👶 semver/patch This is a backwards-compatible fix 🤞 phase/open Post is being triaged manually labels Feb 25, 2024
Copy link

vercel bot commented Feb 25, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
mdx ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 25, 2024 0:58am

@wooorm
Copy link
Member

wooorm commented Feb 25, 2024

Using a function improves performance, that's why it's a function instead of a component. It also allows for diffing; the recommendation is to call the MDXContent yourself if it works even.
The provide function is not always called. And, does not necessarily contain a hook.
Should most users loose performance because of this? Should non-react users, or people who use different or no providers, get a slower experience?

@remcohaszing
Copy link
Member Author

I get your point, though I have my doubts about the significance of the performance impact.

An alternative partial solution could be to let the React and Preact providers default the wrapper comonent to Fragment. This fixes the problem if wrapper is toggled via a provider. It doesn’t fix the problem if the user then explicitly passes a wrapper component via props and toggles that between undefined and a defined value.

@wooorm
Copy link
Member

wooorm commented Feb 26, 2024

I have my doubts about the significance of the performance impact.

You didn’t before? #2029, #2062.


The compiler knows when it injects provideComponents. I am surprised you don’t want to go about it with that info?

@remcohaszing
Copy link
Member Author

I have my doubts about the significance of the performance impact.

You didn’t before? #2029, #2062.

That’s a very different issue. I don’t see how that’s related.


The compiler knows when it injects provideComponents. I am surprised you don’t want to go about it with that info?

I’m not sure what you mean by this. Perhaps I’m missing something you’re seeing?

@wooorm
Copy link
Member

wooorm commented Feb 26, 2024

Huh, I thought it was added in a PR somewhere. I can’t find the trace quickly in xdm other than wooorm/xdm@f0fde3b. Anyway, see #1655 for more on speed.

I’m not sure what you mean by this. Perhaps I’m missing something you’re seeing?

A hook can exist in the provider.
We can support a hook in the provider, but not decrease performance for people that do not use providers, by switching functionality based on whether the compiler is configured with a provider.
You can make the changes in this PR optional.

@remcohaszing
Copy link
Member Author

Ah, I thought you didn’t want this transform in case for example @mdx-js/vue or a third party provider is used as providerImportSource.

Another alternative I’m thinking of is something along these lines:

  function _createMdxContent(props) {
-   const _components = {
-     ..._provideComponents(),
-     ...props.components,
-     p: 'p',
-   }
+   const _components = props.components
  }

  export default function MDXContent(props = {}) {
-   const {wrapper: MDXLayout} = {
+   const components = {
      ..._provideComponents(),
      ...props.components,
+     p: 'p',
    }
+   const MDXLayout = components.wrapper
    return MDXLayout
-     ? <MDXLayout {...props}>
-         <_createMdxContent {...props} />
-       </MDXLayout>
-     : _createMdxContent(props)
+     ? <MDXLayout {...props} components={components}>
+         <_createMdxContent {...props} components={components} />
+       </MDXLayout>
+     : _createMdxContent({...props, components: components})
  }

The benefit is that _provideComponents() is only called once. The downside with this is that the components prop constructed does not have a stable identity.

@wooorm
Copy link
Member

wooorm commented Feb 29, 2024

in case for example @mdx-js/vue

Well, that would be quite nice yeah. That we only have a slow path for providers that might need it. And inline it for those that don’t. But I don’t know how. And generally I’d recommend against providers. So it’s not the common case.

_provideComponents() is only called once

I believe that shouldn’t happen. Component resolution needs to be in the tree at the right place. Otherwise it would break context based APIs: components defined in MDX files (exported from them) also use the provider for “missing” components.

Someone can inject more components in MDXLayout too. That’s why this whole _createMdxContent mess exists...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🗄 area/interface This affects the public interface 🤞 phase/open Post is being triaged manually 👶 semver/patch This is a backwards-compatible fix 🐛 type/bug This is a problem
Development

Successfully merging this pull request may close these issues.

react hmr (fast refresh) breaks when deleting all content of a mdx file
2 participants