简体   繁体   中英

Replacing characters between strings with html tags in react

I'm trying to parse some text so that _this is emphasized!_ is wrapped in <em> tags like so: <em>this is emphasized!</em> .

My component currently looks like this:

export default class TextParser extends React.Component {
  render() {
    let text = this.props.text,
        parsed, regex, paragraphs;

    regex = {
      paragraph: /(?:\r\n){2,}/g,
      emphasize: /\_(.*?)\_/g,
      strong: /\*(.*?)\*/g,
    }

    // Apply regex
    text = text.replace(regex.emphasize, (str) => {
      let parsed = str.substr(1, str.length - 1);

      return ('<em>' + parsed + '</em>')
    })

    paragraphs = text.split(regex.paragraph) || []
    paragraphs = paragraphs.map((text, i) => {
      return (
        <p key={i}>
          {text}
        </p>
      )
    })

    return (
      <div className="document">{paragraphs}</div>
    )
  }
}

This does not work, however the output html displays the tags in plain text instead of using them in the html. This is of course because of sanitization.

I could dangerouslySetInnerHTML but I want to avoid that. How can I replace the underscores between text with <em> tags?

As you noticed, placing the string "<em>" as part of the result of replace just adds that string and not an actual tag.

You will not be able create tags directly inside of replace because that is operating on a string.

Instead, break the string up into separate elements and add the tags where you need them. You already do something like this in the paragraph case.

Because the paragraph case also operates on a string, these kind of operations can only be done nested, since once you complete the operation you no longer have a plain text string, you have an array of objects. So in this example I moved the <em> parsing inside the paragraph parsing.

One last note, I had to modify the regex for emphasize so that it captured the underscores, because I need to check again whether it was a match or not after I have done the split.

let text = this.props.text,
    parsed, regex, paragraphs;

regex = {
  paragraph: /(?:\r\n){2,}/g,
  emphasize: /(\_.*?\_)/g,
  strong: /\*(.*?)\*/g,
}

paragraphs = text.split(regex.paragraph) || []
paragraphs = paragraphs.map((text, i) => {
  return (
    <p key={i}>
      {        
           // Apply regex
           text.split(regex.emphasize).map((str) => {
           let parsed = str.search(regex.emphasize) !== -1 
              ? (<em>{str.substr(1, str.length - 2)}</em>) 
              : str;
            return parsed;
        })}
    </p>
  )
})

return (
  <div className="document">{paragraphs}</div>
)

Based on your comments below, you also want to know how to handle either/or formatting case. So for completeness I have included the code for that here. I chose to combine the formatting patterns into a single regex, and then I explicitly check for '_' or '*' to decide whether to add em or b tags. I then recursively call this when there is a match, in case there are additional matches within. You may choose to clean this up differently, but I hope this helps.

let text = this.props.text,
    parsed, regex, paragraphs;

regex = {
  paragraph: /(?:\r\n){2,}/g,
  formatting: /(\_.*?\_)|(\*.*?\*)/g,
}

  let applyFormatting = (text) => {
    return text.split(regex.formatting).filter(n => n).map((str) => {
    let parsed = str[0] == '_'
        ? (<em>{applyFormatting(str.substr(1, str.length - 2))}</em>)
        : str[0] == '*'
        ? (<b>{applyFormatting(str.substr(1, str.length - 2))}</b>)
        : str;
    return parsed;
  });
};

paragraphs = text.split(regex.paragraph) || []
paragraphs = paragraphs.map((text, i) => {
  return (
    <p key={i}>
      { applyFormatting(text) }
    </p>
  )
})

return (
  <div className="document">{paragraphs}</div>
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM