Заменить часть строки тегом в JSX

Я пытаюсь заменить части строки тегами JSX, например:

render: function() {
    result = this.props.text.replace(":",<div className="spacer"></div>);
    return (
         <div>        
             {result}
         <div>        
    );
}

Но, учитывая, что this.props.text есть Lorem : ipsum, это приводит к

<div>
    Lorem [object Object] ipsum.
</div>

Есть ли способ решить тот или иной способ замены частей строки тегами JSX?

Ответ 1

Когда вы передаете элемент JSX в replace() в качестве второго аргумента, этот элемент преобразуется в строку, потому что replace() ожидает строку в качестве второго аргумента. Что вам нужно сделать, это преобразовать вашу строку в массив строк и элементов JSX. Поэтому ваша переменная result должна содержать что-то вроде ['Lorem ', <div className="spacer"></div>, ' ipsum'].

Что-то вроде этого:

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>
    );
  }
});

Ответ 2

Следующее также должно работать (предполагается ES6). Единственный нюанс заключается в том, что текст фактически завернут внутри элемента DIV и не предшествует ему, предполагая, что вы собираетесь использовать CSS для фактического интервала, это не должно быть проблема.

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

Ответ 3

Принятый ответ - два года. Для этой проблемы была создана проблема # 3368, и на основе решения, предоставленного сотрудником Facebook, работающим над React, была создана реагирующая строка-замена.

Используя ответную строку, вот как вы можете решить вашу проблему

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>
    );
  },
});

Ответ 4

У меня была более распространенная задача - обернуть все (английские) слова пользовательским тегом. Мое решение:

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>

Ответ 5

После некоторых исследований я обнаружил, что существующие библиотеки не соответствуют моим требованиям. Поэтому, конечно, я написал свое собственное:

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

Он очень прост в использовании. Пример вашего примера:

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

Ответ 6

Если вы также захотите сделать замены в рамках замены (например, чтобы выделить условия поиска в URL-адресах), проверьте этот модуль node, который я создал - https://github.com/marcellosachs/react-string-replace-recursively

Ответ 7

Пример с крючками:

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
}

Ответ 8

Написал служебную функцию для 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;
  });
};

Ответ 9

Я пришел к следующему простому решению, которое не включает стороннюю библиотеку или регулярные выражения, возможно, оно все еще может кому-то помочь.

В основном просто используйте .replace() для замены строки обычным html, записанным в виде строки, например:

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

А затем визуализировать его, используя dangerouslySetInnerHTML внутри элемента.

Полный пример:

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
        }} />
    )
}