3

in my React app I am getting some HTML from another server (Wikipedia) and - in this text - want to replace all the links with react-router links.

What I have come up with is the following code:

// Parse HTML with JavaScript DOM Parser
let parser = new DOMParser();
let el = parser.parseFromString('<div>' + html + '</div>', 'text/html');

// Replace links with react-router links
el.querySelectorAll('a').forEach(a => {
  let to = a.getAttribute('href');
  let text = a.innerText;
  let link = <Link to={to}>{text}</Link>;
  a.replaceWith(link);
});
this.setState({
  html: el.innerHTML
})

Then later in render() I then inserted it into the page using

<div dangerouslySetInnerHTML={{__html: this.state.html}} />

The problem is that React JSX is not a native JavaScript element thus not working with replaceWith. I also assume that such a Link object can not be stored as text and then later be restored using dangerouslySetInnerHTML.

So: what is the best way to do this method? I want to keep all the various elements around the link so I cannot simply loop through the links in render()

Jack Dunsk
  • 91
  • 1
  • 11
  • You can make links that utilize the history API in order to "programatically" influence the react-router, the same way when you would want any part of the javascript to change a page without using a `` – Dellirium Feb 20 '19 at 19:57

3 Answers3

4

Umm, you really need to use Link Option 1:

import { renderToString } from 'react-dom/server';
el.querySelectorAll('a').forEach(a => {
  let to = a.getAttribute('href');
  let text = a.innerText;
  const link = renderToString(<Link to={to}>{text}</Link>);
  a.replaceWith(link);
});

Option 2: Use html-react-parser

Subhendu Kundu
  • 3,618
  • 6
  • 26
  • 57
  • 2
    smart move. way better than parsing – FooBar May 05 '20 at 21:25
  • Does that actually work? Since renderToString is designed to be used on the server, presumably the relevant onClick handlers etc. won't be correctly setup for the `Link` component when using this – cdimitroulas Apr 14 '21 at 13:37
3

You can not render <Link> like this. Instead you could try another way:

el.querySelectorAll('a').forEach((a) => {
    a.addEventListener('click', (event) => {
       event.preventDefault()
       const href = a.getAttribute('href');
       // use React Router to link manually to href
    })
})

when click on <a> you routing manually.

See Programmatically navigate using react router

1

You can leverage functionality of html-react-parser

import parse, { domToReact } from 'html-react-parser';
import { Link } from 'react-router-dom';    

function parseWithLinks(text) {
  const options = {
    replace: ({ name, attribs, children }) => {
      if (name === 'a' && attribs.href) {
        return <Link to={attribs.href}>{domToReact(children)}</Link>;
      }
    }
  };
      
  return parse(text, options);
}

And then use it like:

const textWithRouterLinks = parseWithLinks('<a href="/home">Home page</a>');
alit
  • 11
  • 1