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

Getting React Hooks Linting Warning when Using Styled Components #100

Open
danboyle8637 opened this issue Sep 27, 2021 · 4 comments
Open

Comments

@danboyle8637
Copy link

  • mdx-bundler version: 6.0.1
  • node version: 16.3.0
  • npm version: 7.16.0

Relevant code or config

Using NextJS latest version.

export const getStaticProps: GetStaticProps = async ({ params }) => {
  const slug = params?.slug || "";

  const POSTS_PATH = path.join(process.cwd(), "data/blog");

  const getSourceOfFile = (filename: string) => {
    return fs.readFileSync(path.join(POSTS_PATH, filename), "utf8");
  };

  const getSinglePost = async (slug: string) => {
    const source = getSourceOfFile(`${slug}.mdx`);

    const { code, frontmatter } = await bundleMDX(source, {
      cwd: POSTS_PATH,
      esbuildOptions: (options) => {
        options.platform = "node";
        return options;
      },
    });

    return {
      frontmatter,
      code,
    };
  };

  const post = await getSinglePost(slug as string);

  return {
    props: {
      ...post,
    },
  };
};

What you did:

I used Styled Components in an external component. I imported into my mdx file and the page errored out referencing styled components.

I found a solution that said to add this big of code to the options parameter of bundleMDX.

esbuildOptions: (options) => {
        options.platform = "node";
        return options;
      },

What happened:

This worked and my external component using styled components rendered correctly.

However, in my console I get the warning from React...

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.

In the stack trace styled components is referenced again.

Here is my test external component:

import styled from "styled-components";

const Box = styled.div`
  padding: 20px;
  background-color: orange;
  width: 200px;
  height: 150px;
`;

export const TestComponent = () => {
  return (
    <>
      <h1>Other Component</h1>
      <Box />
    </>
  );
};

I am not using any hooks except the useMemo hook when creating the Component from mdx-bundler/client.

Reproduction repository:

https://github.com/danboyle8637/nerds-who-sell/blob/main/pages/blog/%5Bslug%5D.tsx

Suggested solution:

I feel like there must be another option to add to make styled components play nicely with this plugin. When I take the styled components out of my external component... this warning goes away.

@souporserious
Copy link

souporserious commented Jan 31, 2022

I was able to fix this warning by executing the compiled code in an effect. I'm not using mdx-bundler, but I think this should solve it (not-tested):

import { createElement } from 'react'
import { getMDXComponent } from 'mdx-bundler/client'
import useIsomorphicLayoutEffect from 'use-isomorphic-layout-effect'

export function useMDXComponent(mdxSource, props) {
  const [component, setComponent] = React.useState(null)

  useIsomorphicLayoutEffect(() => {
    const component = createElement(getMDXComponent(mdxSource), props)
    setComponent(component)
  }, [mdxSource, props]) // probably need something better than passing full props since it's a shallow diff

  return component
}

Hopefully there's a better way, but at least this works for now 😄

UPDATE:

The above doesn't work with SSR and I couldn't seem to find a way around it with refs. I ended up using a class which seems to work great with no warnings and is SSR friendly 🎉

import * as React from 'react'
import { getMDXComponent } from 'mdx-bundler/client'

export class MDXComponent extends React.Component<{ mdxSource: string }> {
  state = {
    component: getMDXComponent(this.props.mdxSource),
  }

  componentDidUpdate(prevProps) {
    if (prevProps.mdxSource !== this.props.mdxSource) {
      this.setState({ component: getMDXComponent(this.props.mdxSource) })
    }
  }

  render() {
    return React.createElement(this.state.component)
  }
}

// Example
const App = ({ mdxSource }) => <MDXComponent mdxSource={mdxSource} />

@phillip-hogan
Copy link

Thanks for this @souporserious souporserious. This sorted my issue, although I do get an issue when I refresh the page stating the server and client className do not match.

Warning: Prop className did not match. Server: "sc-eCImPb hPZtLc" Client: "sc-gsDKAQ ghVDwf"

Is this something you encountered as well whilst finding a work around for this issue? I'm using Next.js 12.

@souporserious
Copy link

Hm, I can't remember if I ran into that or not 🤔 do you have the babel plugin installed or the new flag set for the SWC plugin?

@phillip-hogan
Copy link

Thanks for the reply :) I have the flag set for the SWC plugin. The only pages on my site which have this warning are ones using mdx bundler and styled components. Next pages with styled components are fine - no warning.

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

3 participants