Replace part of string with tag in JSX

浪子不回头ぞ 提交于 2019-12-03 22:31:31

When you pass a JSX element to replace() as the second argument, that element is converted to a string because replace() expects a string as a second argument. What you need to do is convert your string to an array of strings and JSX elements. So your result variable should contain something like ['Lorem ', <div className="spacer"></div>, ' ipsum'].

Something like this:

function flatMap(array, fn) {
  var result = [];
  for (var i = 0; i < array.length; i++) {
    var mapping = fn(array[i]);
    result = result.concat(mapping);
  }
  return result;
}

var Comp = React.createClass({
  render: function () {
    var result = 'Lorem : ipsum';
    result = flatMap(result.split(':'), function (part) {
      return [part, <div>spacer</div>];
    });
    // Remove the last spacer
    result.pop();
    return (
      <div>        
        {result}
      </div>
    );
  }
});

The following should also work (assumes ES6), The only nuance is that the text is actually wrapped inside a DIV element and not preceded by it, assuming you are going to use CSS for the actual spacing this shouldn't be a problem.

const result = this.props.text.split(':').map(t => { return <div className='textItem'>{t}</div>; });

The accepted answer is two years old. For this problem issue #3368 was created and based on the solution provided by a Facebook employee working on React, react-string-replace was created.

Using react-string-replace, here is how you can solve your problem

const reactStringReplace = require('react-string-replace');

const HighlightNumbers = React.createClass({
  render() {
    const content = 'Hey my number is 555:555:5555.';
    return (
      <span>
        {reactStringReplace(content, ':', (match, i) => (
          <div className="spacer"></div>
        ))}
      </span>
    );
  },
});

I had the more common task - wrap all (English) words by custom tag. My solution:

class WrapWords extends React.Component {
  render() {
    const text = this.props.text;
    const isEnglishWord = /\b([-'a-z]+)\b/ig;
    const CustomWordTag = 'word';

    const byWords = text.split(isEnglishWord);

    return (
    <div>
      {
        byWords.map(word => {
          if (word.match(isEnglishWord)) {
            return <CustomWordTag>{word}</CustomWordTag>;
          }
          return word;
        })
      }
    </div>
    );
    
  }
}

// Render it
ReactDOM.render(
  <WrapWords text="Argentina, were playing: England in the quarter-finals (the 1986 World Cup in Mexico). In the 52nd minute the Argentinian captain, Diego Maradona, scored a goal." />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="react"></div>

After some research I found that existing libraries doesn't fit my requirements. So, of course, I have written my own:

https://github.com/EfogDev/react-process-string

It is very easy to use. Your case example:

let result = processString({
    regex: /:/gim,
    fn: () => <div className="spacer"></div>
})(this.props.test);

If you'd also like to be able to make replacements within replacements (for example, to highlight search terms within urls), check out this node module I created - https://github.com/marcellosachs/react-string-replace-recursively

Example with hooks:

import React, { useEffect, useState, useRef } from 'react'

export function Highlight({ value, highlightText }) {
  const [result, resultSet] = useState(wrap())

  const isFirstRun = useRef(true) 

  function wrap() {
    let reg = new RegExp('(' + highlightText + ')', 'gi')
    let parts = value.split(reg)

    for (let i = 1; i < parts.length; i += 2) {
      parts[i] = (
        <span className='highlight' key={i}>
          {parts[i]}
        </span>
      )
    }
    return <div>{parts}</div>
  }

  useEffect(() => {
    //skip first run
    if (isFirstRun.current) {
      isFirstRun.current = false
      return
    }
    resultSet(wrap())
  }, [value, highlightText])

  return result
}

Wrote a utility function for jsx.

const wrapTags = (text: string, regex: RegExp, className?: string) => {
  const textArray = text.split(regex);
  return textArray.map(str => {
    if (regex.test(str)) {
      return <span className={className}>{str}</span>;
    }
    return str;
  });
};

I have come to following simple solution that does not include third party library or regex, maybe it can still help someone.

Mainly just use .replace() to replace string with regular html written as string, like:

text.replace('string-to-replace', '<span class="something"></span>')

And then render it using dangerouslySetInnerHTML inside an element.

Full example:

const textToRepace = 'Insert :' // we will replace : with div spacer
const content = textToRepace.replace(':', '<div class="spacer"></div>') : ''

// then in rendering just use dangerouslySetInnerHTML
render() {
    return(
        <div dangerouslySetInnerHTML={{
            __html: content
        }} />
    )
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!