简体   繁体   中英

React - highlight matching substring using array of characters and jsx tags

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...

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM