Reputation: 2351
I want to display the highlight of a substring. Currently, the JSX resolves to the following:
Here's the algorithm that produces this string; maybe you can see my strategy and tell me if it's a dumb one:
/**
* prepares string with JSX span tags to assist with highlighting of search result matches
* @param {string} string to find the match within
* @param {string} matchString the search query string that we are highlighting against
*
* @returns {Array} an array containing characters and JSX for syntax highlighting
*/
export const matchHighlighting = (name, matchString) => {
let renderArray = []
let buffer = []
let nameArray = name.split("")
let matchIdx = 0;
for (var i = 0; i < nameArray.length; i++) {
if (matchString[0] == nameArray[i]) {
let matching = true;
matchIdx = 0;
/* start testing for match, if successfully get through match, place inside tag
and push that to the render array */
while (matching) {
if (matchString[matchIdx] == nameArray[i]) {
buffer.push(nameArray[i]);
if (matchIdx + 1 == matchString.length) {
/* complete match add JSX */
renderArray.push(<span className="search-match-string">
{ buffer }
</span>);
matching = false;
buffer = [];
break;
}
i++
matchIdx++
} else {
/* no longer match, clean up matching state
and return to normal iteration */
matching = false;
buffer.push(nameArray[i]);
renderArray = renderArray.concat(buffer);
}
}
} else { /* no match, all is chill */
renderArray.push(nameArray[i]);
}
}
return renderArray
}
I am struggling quite a bit on finding a solution to the key error that this strategy produces:
Each child in an array or iterator should have a unique "key" prop
How would I 'join' these character arrays and span tags in a way where the tags wouldn't be subsumed in the string?
Or
How would I assign keys to the individual characters and jsx tags that make up this array?
Those are at least the two solutions I am trying to figure out, and am not making very good ground on it. I suppose I am going to continue trying to solve the first strategy (ie. not having an array of characters but a single string) but I am lost on how to include the JSX tags...
Upvotes: 0
Views: 1503
Reputation: 33171
So, I think it will be much easier to leverage indexOf
to do the matching, and build an array of matches/non matches. Then turn this array into the appropriate <span>
s. For example:
function buildPieces(str, match) {
var pieces = []
var index, pos = 0
while ((index = str.indexOf(match, pos)) >= 0) {
if (pos !== index) {
pieces.push(str.substr(pos, index - pos))
}
pieces.push(match)
pos = index + match.length
}
if (pos < str.length) {
pieces.push(str.substr(pos));
}
return pieces;
}
const str = 'This is a test string with test in it twice.'
const matchStr = 'test'
const pieces = buildPieces(str, matchStr)
Then inside your component
<span>{pieces.map(item => {
if(item === matchStr){
return <span className="match">{item}</span>
}else{
return item
}
})}</span>
You can see a demo here
Upvotes: 3