I had to implement a React component which takes text and adds links to the detected URLs:
<AutoLink>
  This text contains link to http://example.com and https://example.com
</AutoLink>
This generates the following HTML:
<p>
  This text contains link to <a href="http://example.com" target="_blank">http://example.com</a> and <a href="https://example.com" target="_blank">https://example.com</a>
</p>
In order to build this component, I had to use а little know feature of the String.prototype.split method. Most people know that if you have the following string a,c,d,e you can split it by ,, you get an array:
"a,b,c".split(/,/) // => ["a", "b", "c"]
Today I learned, that you can actually include the separator in the results:
"a,b,c".split(/(,)/) // => ["a", ",", "b", ",", "c"]
This uses the regular expression group feature.
Using this feature, I created a mapLinks function. It takes the text and yields the links it detects.
const EXP = /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi; 
export function mapLinks(text, fn)) {
  // this splits the text, by links but it still keeps those links in the result
  return text.split(EXP).map((chunk, i) => {
    if (chunk.match(EXP)) {
      return fn(chunk, i);
    }
    return chunk;
  });
Then I just used this function for the component itself:
export default React.memo(({ children }) => (
  <p>
    {mapLinks(children, (url, i) => (
      <a href={url} target="_blank" key={i}>
        {url}
      </a>
    ))}
  </p>
)}
    
Top comments (0)