question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Generated JSX should not define component on rerender

See original GitHub issue

Initial checklist

Affected packages and versions

^2.0.0-rc.2 (#1810)

Link to runnable example

No response

Steps to reproduce

Run the following script:

import { compile } from '@mdx-js/mdx';

const { value } = await compile('hello', { jsx:true })

console.log(value)

The jsx option is there for readability of the output. The issue applies if it’s either true or false.

Expected behavior

The output doesn’t define a component inside a component. This is a React anti-pattern, because it causes unnecessary rerenders

The following Google search yields some articles that explain why this is a bad idea in React: https://www.google.com/search?q=react+create+component+inside+component. Example article: https://dev.to/borasvm/react-create-component-inside-a-component-456b. The same principles apply to Preact.

Earlier the output was ok:

/*@jsxRuntime automatic @jsxImportSource react*/
function MDXContent(props = {}) {
  const _components = Object.assign({
    p: "p"
  }, props.components), {wrapper: MDXLayout} = _components;
  const _content = <><_components.p>{"hello"}</_components.p></>;
  return MDXLayout ? <MDXLayout {...props}>{_content}</MDXLayout> : _content;
}
export default MDXContent;

Alternative output that’s also ok for React/Preact, although I recall it’s troublesome for some frameworks:

/*@jsxRuntime automatic @jsxImportSource react*/
import { Fragment as _Fragment } from 'react/jsx-runtime.js'
function MDXContent(props = {}) {
  const _components = Object.assign({
    p: "p"
  }, props.components), {wrapper: MDXLayout = _Fragment} = _components;
  return <MDXLayout><_components.p>{"hello"}</_components.p></MDXLayout>;
}
export default MDXContent;

Actual behavior

The output does define a component inside a component, which causes unnecessary rerenders.

/*@jsxRuntime automatic @jsxImportSource react*/
function MDXContent(props = {}) {
  const {wrapper: MDXLayout} = props.components || ({});
  return MDXLayout ? <MDXLayout {...props}><_createMdxContent /></MDXLayout> : _createMdxContent();
  function _createMdxContent() {
    const _components = Object.assign({
      p: "p"
    }, props.components);
    return <_components.p>{"hello"}</_components.p>;
  }
}
export default MDXContent;

Runtime

Node v16

Package manager

npm v8

OS

Linux

Build and bundle tools

Other (please specify in steps to reproduce)

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:7 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
wooormcommented, Jun 10, 2022

This sounds like a whole, unrelated, problem. Perhaps you can open a discussion and share much more about what you want to do.

I don’t understand why MDXPType is added instead of the new MDX AST nodes. You can walk the AST, “split” on thematic breaks, and check if there’s only one thing in them, in which case you wrap content into a JSX element.

0reactions
0phoffcommented, Jun 10, 2022

I don’t believe traversing the react tree is the way to go, generally. We have ASTs and they’re much better.

I completely agree and was looking for a way to perform this as a rehype/recma plugin, but couldn’t figure out how to do the following:

Users of my library can mark components as being a “slide layout component”. If such a component is used as the sole component of a slide (between 2 <hr>), then I consider that to be a slide. If there are multiple components between the <hr> tags or the component is not defined as a layout, then I wrap the content in a default slide layout.

The way I mark a component as a slide layout is by adding a property MDXPType on the function and thus I think I can only perform this check at runtime. The only way I can think of to do this with unified at build time, is to either pass a list of slide layouts to the plugin or have a rule that slide layout components should always end in “SlideLayout”. I don’t really like either of these options and thus implemented my parsing at runtime in the client 😅

Read more comments on GitHub >

github_iconTop Results From Across the Web

5 Ways to Avoid React Component Re-Renderings
1. Memoization using useMemo() and UseCallback() Hooks ... Memoization enables your code to re-render components only if there's a change in the props....
Read more >
How to avoid unnecessary re-render of a component in React?
The reason it re-renders even though the props don't change is because you are changing state on the Parent component.
Read more >
When does React re-render components? - Felix Gerschau
React DevTools lets you highlight renders under Components -> View Settings -> Highlight updates when components render. This will show you the ...
Read more >
How and when to force a React component to re-render
Generally, forcing a React component re-render isn't best practice, even when React fails to update the components automatically. So, before ...
Read more >
When does React render your component? - Zhenghao
the parent component got rendered and your component doesn't meet the criteria for bailing out on re-rendering, where all these four conditions ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found