Reputation: 1897
Shared question from: https://github.com/iansinnott/react-string-replace/issues/33
react-string-replace: https://github.com/iansinnott/react-string-replace
I'm having trouble using reactStringReplace
to format text with bold/italic tags. I have it set up as follows:
var text1 = "[b]Testing bold [i]and italic[/i] tags[/b]"
var text2 = "[b]Testing bold [i]and italic tags[/b][/i]"
let replaced = reactStringReplace(text1, /\[b\]([\s\S]+)\[\/b\]/g, (match, i) => {
return <b key={i}>{match}</b>
})
replaced = reactStringReplace(replaced, /\[i\]([\s\S]+)\[\/i\]/g, (match, i) => {
return <i key={i}>{match}</i>
})
// result (html)
<b>Testing [i]bold and italic[/i] tags</b>
<b>Testing [i]bold and italic tags</b>[/i]
I'm not sure if this is a problem with reactStringReplace
, with the regex I'm using, or something else. If I apply the italic replace first, I get italic tags where I'd expect them to be, but the [b] tags remain unchanged. Is this use case possible using reactStringReplace
or do I need to use dangerouslySetInnerHtml
?
Bonus: is reactStringReplace
capable of handling unbalanced tags, or improperly formatted tags as in text2
or should I be doing some pre-processing to ensure the strings are properly balanced and formatted?
Upvotes: 2
Views: 1496
Reputation: 18950
Your sample has multiple issues,
<i key={match + i}>{match}</i>
(see below)+
quantifier may match too much.;
s, etc.Ad. 1: Quoting from the full example:
In many React examples you will see the
i
orindex
variable used
as the key for JSX tags (such as the<a>
tags in this example), however in this case we are iterating in three separate loops. This means that we cannot usekey={i}
because all three JSX tags could get the same key.
Ad. 2: The most crucial part here, however, is your matches are overlapping, and this does not go well with reactStringReplace
.
A demonstration:
const text = 'Hey @ian_sinn#reactconf';
let replacedText;
// Match @-mentions
replacedText = reactStringReplace(text, /@(\w+)/g, (match, i) => (
<b key={match + i}>@{match}</b>
));
// Match hashtags
replacedText = reactStringReplace(replacedText, /#(\w+)/g, (match, i) => (
<a key={match + i} href={`https://twitter.com/hashtag/${match}`}>#{match}</a>
));
We have distinct matches in this sample: first, @ian_sinn
is matched and replaced, then #reactconf
, thus the replacement works as intended; however, if you change the first regex replacing \w
with a .
you get an overlapping match: Now, the second replacement will not be applied.
It works if you avoid overlapping matches:
render() {
const text = '[b]Testing bold [i]and italic[/i] tags[/b]';
let replacedText;
replacedText = reactStringReplace(text, /\[b\]((?:(?!\[\/b\]|\[i\]|\[\/i\]).)*)/g, (match, i) => (
<b key={match + i}>{match}</b>
));
replacedText = reactStringReplace(replacedText, /\[i\]((?:(?!\[\/b\]|\[i\]|\[\/i\]).)*)/g, (match, i) => (
<b><i key={match + i}>{match}</i></b>
));
replacedText = reactStringReplace(replacedText, /\[\/i\]((?:(?!\[\/b\]|\[i\]|\[\/i\]).)*)/g, (match, i) => (
<b key={match + i}>{match}</b>
));
replacedText = reactStringReplace(replacedText, /\[\/b\]((?:(?!\[\/b\]|\[i\]|\[\/i\]|$).)*)$/g, (match, i) => (
<b key={match + i}>{match}</b>
));
return (
<div>
{replacedText}
</div>
);
},
});
But this requires you to throw more sophisticated regex at the problem, gives you ugly markup, and arguably, defeats the purpose of the whole adventure...
Ad. 3: Compare this with a simple vanilla regex replace using an ungreedy match: [\s\S]+?
const str = `[b]Testing bold [i]and italic[/i] tags[/b]`;
const result = str.replace(/\[b\]([\s\S]+?)\[\/b\]/gm, `<b>$1</b>`)
.replace(/\[i\]([\s\S]+?)\[\/i\]/gm, `<i>$1</i>`);
console.log('Substitution result: ', result);
Verdict: Sad, but now official. As of now, reactStringReplace does not handle nested replacements:
since replacements can change the data type to something other than strings. If you look here you'll see that it only runs replacements on strings. Once your first replacement has been run the element in the result array will be an object (i.e.
<b>...</b>)
so no replacements will be run on the inner string.
Upvotes: 1