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

Unable to use state hook in MDX #191

Open
js-egton opened this issue Oct 25, 2022 · 2 comments
Open

Unable to use state hook in MDX #191

js-egton opened this issue Oct 25, 2022 · 2 comments

Comments

@js-egton
Copy link

  • mdx-bundler version: ^9.0.1
  • node version: 17.6.0
  • npm version: 7.5.2 (using pnpm)

Relevant code or config:

mdxSource is the code returned from bundleMDX with the following plugins:

  • remarkGfm
  • remarkMdx
  • rehypeSlug
{/* Main render component */}
const MDXComponent = useMemo(() => getMDXComponent(mdxSource), [mdxSource]);
let componentsToLoad = {
    Example,
    JSXHighlighter,
    (etc)
};

return (
  <MDXComponent components={componentsToLoad} />
);

{/* MDX file */}
import { useState } from "react";
export const [dropdownMenuOpen, setDropdownMenuOpen] = useState(false);
...
<Button onClick={() => setDropdownMenuOpen(!dropdownMenuOpen)}>Button</Button>

What you did:

Added a state hook (with useState) inside MDX file, then triggered a state update on the rendered output.

What happened:

When loading the page initially, Next.JS outputs the following warning to the terminal:

Warning: Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. You can only call Hooks at the top level of your React function. For more information, see https://reactjs.org/link/rules-of-hooks

After clicking the button to trigger the state update, the rendered page errors out with the following:

Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.

Reproduction repository:

N/A

Problem description:

By using useMemo to create the <MDXComponent> constant, the useState hook is being nested; this goes against React's Rules of Hooks and causes the code to throw an error. Removing the useMemo call suppresses the terminal error, but the page then becomes unresponsive after triggering a state update (presumably as is tries to remake MDXComponent over and over).

Suggested solution:

Unknown.

@hahnbeelee
Copy link

React hooks can only be made within React components. The contents of an MDX file are not inherently a React component. Your example looks like it'd be more suitable if you made a separate Button component in a jsx file then imported it into your MDX file to use it.

@js-egton
Copy link
Author

js-egton commented Nov 7, 2022

Your example looks like it'd be more suitable if you made a separate Button component in a jsx file then imported it into your MDX file to use it.

The Button already is a separate component 🙂 However it cannot be imported into the MDX directly because it is not a default export of the Button JSX file, and this package does not support import { X, Y } from "Z" style imports (as far as I could tell), hence the use of the components prop on the <MDXBundler> component to pull external components into the MDX.

React hooks can only be made within React components. The contents of an MDX file are not inherently a React component.

I'm aware that MDX != component, and that this is a fairly odd edge case usage to be honest! For the moment we have a workaround that doesn't use MDX, and it's working out okay for us so far 👍

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

No branches or pull requests

2 participants