Building Autolink Component in React
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;
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>
));