2

I have a use case where I need to identify phone numbers in a string and convert them to a clickable link. Using an external library is out of scope currently.

Here is what I've done so far:

String: "This is my phone number - 1234567890" I can extract 1234567890 as a phone number using a regex pattern.

I now want to replace it in the original string, so this is what I did:

const string = "This is my phone number - 1234567890"
const number = "1234567890"
const newString = '<a href="#">' + number + '</a>'
number = number.replace(number, newString);

When I do this, instead of getting the phone number as a hyperlink, my output is like this:

This is my phone number - <a href="#">1234567890</a>

If I create my newString without quotes, like this

const newString = <a href="#">number</a>

my output is like this:

This is my phone number - [object Object]

How do I make this a clickable link?

2bornot2b
  • 83
  • 1
  • 10

3 Answers3

2

You'll need to replace to wrap the phone numbers with links, and then use dangerouslySetInnerHTML to add to React components.

Note: It's called dangerouslySetInnerHTML because you open yourself to XSS attacks from user generated content. You should sanitise the strings first.

const Linkify = ({ text, pattern, formatter }) => {
  const __html = text.replace(pattern, formatter);

  return <div dangerouslySetInnerHTML={{ __html }} />;
};

const text = 'This is my phone number - 1234567890';
const pattern = /\d+/g;
const formatter = str => `<a href="#${str}">${str}</a>`;

ReactDOM.render(
  <Linkify text={text} pattern={pattern} formatter={formatter} />,
  root
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>

Without using dangerouslySetInnerHTML the main problem is breaking the string into links/non links strings. This can be done by using the code in this answer. I've updated the code to mark string as matches, so you can format them accordingly.

const Linkify = ({ text, pattern, formatter }) => {
  const arr = getSegments(pattern, text);

  return arr.map(formatter);
};

const text = 'This is my phone number - 1234567890, and his is 34234123142';
const pattern = /\d+/g;
const formatter = ({ text, match }) => match ? <a href={`#${text}`}>{text}</a> : text;

ReactDOM.render(
  <Linkify text={text} pattern={pattern} formatter={formatter} />,
  root
);

function getSegments(rex, str) {
  const segments = [];
  let lastIndex = 0;
  let match;
  rex.lastIndex = 0; // In case there's a dangling previous search
  while (match = rex.exec(str)) {
    if (match.index > lastIndex) {
      segments.push({ text: str.substring(lastIndex, match.index) });
    }
    segments.push({ text: match[0], match: true });
    lastIndex = match.index + match[0].length;
  }
  if (lastIndex < str.length) {
    segments.push({ text: str.substring(lastIndex) });
  }
  return segments;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
1

You don't really want to set a 'string' to const newString, I would call this more an element. You can do it like this:

const phoneEl = (
  <a href="#">
    {number}
  </a>
);

Where number is the phone number stored in a variable called number.

Then you would write:

<p>My phone number is - {phoneEl}</p>

I'd also suggest

<href={`tel:${number}`}>

if you wanted people to be able to click the link and dial.

Brett East
  • 4,022
  • 2
  • 20
  • 31
  • This is the best answer which takes React's best practices into account. Only missing the brackets: ```href={`tel:${number}`}``` (upvoted anyway) – Emile Bergeron Oct 18 '19 at 20:40
  • 1
    @EmileBergeron yeah, oversight haha, I've fixed it. Not sure why it's been downvoted with no comment :/ – Brett East Oct 18 '19 at 20:42
-1

const string = document.querySelector("body");
const text = string.innerText;

const textArr = text.split(" ").map(item => {
  if(item.match(/^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im)){
    item = `<a href="tel:${item}">${item}</a>`;
  }
  return item;
});

const newText = textArr.join(" ");

console.log({ newText })

string.innerHtml = newText;
<p class="body">
  This is my phone number - 555-555-5555
</p>
Mark
  • 1,610
  • 1
  • 14
  • 27