How To Highlight Matches Within A String With Jsx?
Solution 1:
Writing your own highlighting code could lead down a rabbit hole. In my answer, I assume only simple text (no HTML within the strings, no charset edge cases) and valid non-escaped RegExp
pattern string.
Instead of building a new string, you could build a new array, in which you could put JSX.
A React component can also return an array of elements:
render() { // No need to wrap list items in an extra element!return [ // Don't forget the keys :)<likey="A">First item</li>, <likey="B">Second item</li>, <likey="C">Third item</li>, ]; }
The logic behind
As a simple proof of concept, here's the logic we could use:
constdefaultHighlight = s => <em>{s}</em>;
// Needed if the target includes ambiguous characters that are valid regex operators.constescapeRegex = v => v.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
/**
* Case insensitive highlight which keeps the source casing.
* @param {string} source text
* @param {string} target to highlight within the source text
* @param {Function} callback to define how to highlight the text
* @returns {Array}
*/consthighlightWord = (source, target, callback) => {
const res = [];
if (!source) return res;
if (!target) return source;
const regex = newRegExp(escapeRegex(target), 'gi');
let lastOffset = 0;
// Uses replace callback, but not its return value
source.replace(regex, (val, offset) => {
// Push both the last part of the string, and the new part with the highlight
res.push(
source.substr(lastOffset, offset - lastOffset),
// Replace the string with JSX or anything.
(callback || defaultHighlight)(val)
);
lastOffset = offset + val.length;
});
// Push the last non-highlighted string
res.push(source.substr(lastOffset));
return res;
};
/**
* React component that wraps our `highlightWord` util.
*/constHighlight = ({ source, target, children }) =>
highlightWord(source, target, children);
constTEXT = 'This is a test.';
constExample = () => (
<div><div>Nothing: "<Highlight />"</div><div>No target: "<Highlightsource={TEXT} />"</div><div>Default 'test': "<Highlightsource={TEXT}target="test" />"</div><div>Multiple custom with 't':
"<Highlightsource={TEXT}target="t">
{s => <spanclassName="highlight">{s}</span>}
</Highlight>"
</div><div>Ambiguous target '.':
"<Highlightsource={TEXT}target=".">
{s => <spanclassName="highlight">{s}</span>}
</Highlight>"
</div></div>
);
// Render itReactDOM.render(
<Example />,
document.getElementById("react")
);
.highlight {
background-color: yellow;
}
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script><divid="react"></div>
No need to use dangerouslySetInnerHTML
here.
This highlightWord
function can take any function to wrap the matched string.
highlight(match, value) // default to `s => <em>{s}</em>`// orhighlight(match, value, s =><spanclassName="highlight">{s}</span>);
I'm doing minimal regex string escaping based on another answer on Stack Overflow.
The Highlight
component
As shown, we can create a component so it's "more react"!
/**
* React component that wraps our `highlightWord` util.
*/const Highlight = ({ source, target, children }) =>
highlightWord(source, target, children);
Highlight.propTypes = {
source: PropTypes.string,
target: PropTypes.string,
children: PropTypes.func,
};
Highlight.defaultProps = {
source: null,
target: null,
children: null,
};
export default Highlight;
It uses a render prop, so you'd have to change your rendering to:
<ul>
{matches.map((match, idx) => (
<likey={idx}><Highlightsource={match}target={value}>
{s => <strong>{s}</strong>}
</Highlight></li>
))}
</ul>
Solution 2:
You just append your mapper as children inside your auto complete component.
<CustomAutocomplete>
<ul>
{
matches.map(function(match, idx){
let re = new RegExp(value, 'g');
let str = match.replace(re, '<b>'+ value +'</b>');
return (<li key={idx}>{str}</li>)
})
}
</ul>
</CustomAutocomplete>
Post a Comment for "How To Highlight Matches Within A String With Jsx?"