Skip to content

TS Error : instanceof Element always return false with Next.js #221

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

Closed
mrskiro opened this issue Jan 29, 2021 · 18 comments · Fixed by #296
Closed

TS Error : instanceof Element always return false with Next.js #221

mrskiro opened this issue Jan 29, 2021 · 18 comments · Fixed by #296
Labels
question Further information is requested

Comments

@mrskiro
Copy link

mrskiro commented Jan 29, 2021

Reproducible Demo

https://codesandbox.io/s/quizzical-lake-16q4o?file=/pages/index.tsx

When replacing to a custom component, false will always be returned.

I am currently using the any type.
However, is there any other solution other than the any type?

@remarkablemark remarkablemark added the question Further information is requested label Feb 1, 2021
@remarkablemark
Copy link
Owner

remarkablemark commented Feb 1, 2021

Thanks for opening the issue @purp1eeeee

In your CodeSandbox example, you're not returning a React element so replace won't work. See readme.

So can you replace the logs with a return of your custom element?

export default function IndexPage() {
   const html = "<h1>hoge</h1>";
   const options: HTMLReactParserOptions = {
     replace: (domNode) => {
-      console.log(domNode);
-      console.log(domNode instanceof Element);
+      if (domNode.name === "h1") {
+        return <h1>replaced</h1>;
+      }
     }
   };
   return <div>{parse(html, options)}</div>;
 }

See updated CodeSandbox.

@kakaeriel
Copy link

I following this instruction, and I also have same problem, I can't get property attribs from domNode.

Because typescript check domNode is instance of DOMNode, not Element type, so that the error occurs, because DOMNode hasn't attribs property.

And if you check with this condition console.log(domNode instanceof Element) the return always false, because instance of domNode is DOMNode.

I have solution for this, maybe can help. I defined the Element type at the parameter domNode.

import parse, { HTMLReactParserOptions } from "html-react-parser";
import { Element } from "domhandler/lib/node";

export function contentHandler(postContent: string) {
  const options: HTMLReactParserOptions = {
    replace: (domNode: Element) => {
      if (domNode.attribs) {
        if (domNode.attribs.id === 'shortcode') {
          return <div className="leadform">Shortcode</div>;
        }
      }
    },
  };

  return parse(postContent, options);
}

@wheredoesyourmindgo
Copy link

wheredoesyourmindgo commented Feb 2, 2021

I can confirm I'm having the same issue with a Next.js project; Typescript type-casting domNode seems to be the only work around after upgrading html-react-parser.

@mrskiro
Copy link
Author

mrskiro commented Feb 2, 2021

@kakaeriel
so good! thank you👍

@sarah-j-smith
Copy link

Having these issues with a Typescript project. Like @kakaeriel I'm following this instruction about replace including the section on Typescript.

The solution from @kakaeriel works for me. The instanceof operator doesn't work (Element is not a type) and after hours browsing through node_modules looking for a type definition, finding this workaround is a life-saver.

@remarkablemark
Copy link
Owner

remarkablemark commented Feb 2, 2021

Thanks for providing an alternative solution @kakaeriel. Would you be interested in creating a PR to improve the README.md documentation?

@kakaeriel
Copy link

@remarkablemark Yes sure! I will create a PR, ASAP.

@Naxos84
Copy link

Naxos84 commented Feb 22, 2021

Wow. I have the same issue using NextJS.

Because typescript check domNode is instance of DOMNode, not Element type, so that the error occurs, because DOMNode hasn't attribs property.

So why does this only occur in a NextJS app.
I setup a react-scripts typescript app and there it works.
https://codesandbox.io/s/html-parsing-and-replacing-elements-yjtv7?file=/src/index.tsx

@remarkablemark
Copy link
Owner

I'm not sure @Naxos84. I bet Next.js has a different TypeScript configuration. Please check out @kakaeriel's solution for a workaround.

@kakaeriel let me know if you're able to open a PR to update the README.md with your workaround. If you're unable to do it, I can do it. Thanks!

@Naxos84
Copy link

Naxos84 commented Feb 23, 2021

I don't think that it is NextJs related.
I tried a little more and voila it worked somehow.
I changed @remarkablemark codesandbox and added console.log(domElement.constructor) as mentioned on Stack Overflow.

The result looks as follows:
grafik

So I don't understand why it is suddenly working!?

Though it is not working in "my" nextjs project.
Update: And now it's not working again....

@Naxos84
Copy link

Naxos84 commented Feb 23, 2021

I suggest using a custom typeguard.

const isElement = (domNode: DOMNode): domNode is Element => {
    const isTag = domNode.type === "tag";
    const hasAttributes = (domNode as Element).attribs !== undefined;

    return isTag && hasAttributes;
};
const parserOptions: HTMLReactParserOptions = {
    replace: domNode => {
        if (isElement(domNode)) {
            return <p>It's an element.</p>;
        } else {
            return <p>It's something else</p>;
        }
    },
};

@natterstefan
Copy link
Contributor

natterstefan commented Apr 23, 2021

Hey everyone, I was able to fix this in Next.js 10.x with this solution. It's a public Gist where you can find my current implementation details. The solution has zero type errors in my setup.

@remarkablemark
Copy link
Owner

@natterstefan Thanks for sharing your solution!

@vladpuz
Copy link

vladpuz commented May 9, 2021

I have the same problem with NextJS. Element is always undefined. I typed domNode as any and converted the options type as HTMLReactParserOptions. Element from domhandler doesn't work with SSR?

const options = {
  replace: (domNode: any) => {
      // ...
  }
} as HTMLReactParserOptions;

@roybcr
Copy link

roybcr commented Jun 12, 2021

I was able to fix this with the code below, hope it would help

Screen Shot 2021-06-12 at 20 32 31

@remarkablemark
Copy link
Owner

#296 released in 1.4.0

@adamklepacz
Copy link

I'm facing the same issue with replace the function by using your library in Next.js project.
This is my code, where I'm trying to find all of the anchor tags and set properties to some DOM attributes. But it does never go into if block because the condition always returns false.

The version of HTML-React-parser that I'm using is 1.4.0
Any advice?

import parse, {
  attributesToProps,
  domToReact,
  HTMLReactParserOptions,
  Element,
} from 'html-react-parser';

export const ReplaceParser = ({ payload }: { payload: string }) => {
  const options: HTMLReactParserOptions = {
    replace: (domNode) => {
      console.log(domNode instanceof Element); // always returns false

      if (domNode instanceof Element && domNode.name === 'a') {
        const props = attributesToProps(domNode.attribs);

        return (
          <a {...props} rel="noopener noreferrer" target="_blank">
            {domToReact(domNode.children)}
          </a>
        );
      }
    },
  };

  return <>{parse(payload, options)}</>;
};

@tomconroy
Copy link

It's possible to use duck typing to get domNode as an Element:

    parse(html, {
      replace: (domNode) => {
        if ('name' in domNode && 'attribs' in domNode) {
          // domNode is an Element
        }
      },
    });

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

Successfully merging a pull request may close this issue.