Reputation: 3756
In the example below, I'd like to find all strings within delimiters :; and replace them with HTML Mark tags around each match. If it works correctly, those strings should be highlighted in yellow because of the Mark tag. Instead, my tags aren't being expanded. Is there a way to do this in JSX?
function App() {
let inputStr = 'This is a :test; of :highlighting;'
return (
<div>
{HighlightText(inputStr)}
</div>
);
}
function HighlightText(str) {
let MyTag = 'Mark';
var matches = str.split(/[:;]/);
let outputStr = '';
matches.forEach(m => {
const test = ':' + m + ';';
if (str.includes(test)) {
outputStr += <MyTag> + m + </MyTag>;
} else {
outputStr += m;
}
});
return outputStr;
}
ReactDOM.render(
<App />,
document.getElementById("react")
);
<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="react"></div>
Upvotes: 1
Views: 92
Reputation: 15530
As being said in the comments to another answer, I would avoid dangerouslySetInnerHTML
for security considerations.
Instead, you may break your string into array of highlighted and non-highlighted sub-strings and render those either as <mark>
or regular <span>
elements, respectively:
const { render } = ReactDOM,
rootNode = document.getElementById('root'),
testString = `This is a :test; of :highlighting;`
const App = () => {
const subStrings = testString.split(/(:.*?;)/)
return (
<div>
{
subStrings.map((s,key) =>
/(:.*;)/.test(s) ?
<mark {...{key}}>{s.slice(1,-1)}</mark> :
<span>{s}</span>
)
}
</div>
)
}
render (
<App />,
rootNode
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
Upvotes: 2
Reputation: 51766
Use the /:([^:;]*);/g
with String.prototype.split()
to get an array like this:
["This is a ", "test", " of ", "highlighting", ""]
Then you can use Array.prototype.flatMap()
to group it like this:
[["This is a ", "test"], [" of ", "highlighting"], [""]]
Then finally you can use Array.prototype.map()
to return a <React.Fragment />
for each inner array above:
ReactDOM.render(<App />, document.getElementById('react'));
function App() {
const inputStr = 'This is a :test; of :highlighting;';
return (
<div>{highlightText(inputStr)}</div>
);
}
function highlightText(str) {
const MyTag = 'Mark';
const matches = str.split(/:([^:;]*);/g);
const groups = matches.flatMap(
(value, index, array) => (
index % 2 === 0
? [array.slice(index, index + 2)]
: []
)
);
return groups.map(
([prefix, text], index) => (
<React.Fragment key={index}>
{prefix} {text && <MyTag>{text}</MyTag>}
</React.Fragment>
)
);
}
// example function demonstrating that `MyTag` resolves to `<Mark />`
function Mark({ children }) {
return (
<mark>{children}</mark>
);
}
<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="react"></div>
Upvotes: 1