1

I'm trying to provide search suggestions to users where the value the user has inputted into the search field is highlighted in the suggestion. For example, if we had an array of city name strings, and the user typed "new" into the input, I would want to suggest something like "New York City."

My vanilla JS solution:

const cities = [ 'New York City', 'Boston', 'Seattle', 'Miami' ];
const input = document.querySelector('input');
const searchValue = input.value;
const searchRegEx = new RegExp(searchValue, 'gi');
const filteredCities = cities.filter(city => city.match(searchRegEx));
const suggestions = filteredCities.map(city => city.replace(searchRegEx, '<span class="highlight">$&</span>')).join('');

// Insert suggestions into DOM here...

The problem is I'm working in React and the string of HTML is escaped. I know I can use the dangerouslySetInnerHTML prop, but it seems discouraged. Is this a valid use case or is there a better way? Thanks for any help!

Tom T
  • 314
  • 2
  • 12
  • 1
    Does this answer your question? [Dynamically wrap a js string by span tags which can be dangerously rendered in React](https://stackoverflow.com/questions/63606423/dynamically-wrap-a-js-string-by-span-tags-which-can-be-dangerously-rendered-in-r) – Lionel Rowe Jan 15 '21 at 21:55
  • There's no need to use dangerouslySetInnerHTML for this. I'm voting to reopen. – Nicholas Tower Jan 15 '21 at 22:03
  • If you're working in React then it would be good to see some React code. Maybe [this thread](https://stackoverflow.com/questions/29652862/highlight-text-using-reactjs) and [this lib](https://github.com/bvaughn/react-highlight-words) will give you some ideas. – lawrence-witt Jan 15 '21 at 22:05
  • Maybe use the server to send results with the relevant style (in your case maybe u can say isBold) and then apply the styles to the text dynamically. Dont really need `dangerouslySetInnerHTML` for this – Rohit Kashyap Jan 15 '21 at 22:05
  • @NicholasTower The duplicate flag was based on my accepted answer to that question, which doesn't use `dangerouslySetInnerHTML`. It's also probably worth mentioning that React's `dangerouslySetInnerHTML` is no _more_ dangerous than directly setting `el.innerHTML` in vanilla JavaScript. It's just that React is more explicit about the danger. – Lionel Rowe Jan 16 '21 at 14:58
  • @LionelRowe you're right, i should have looked in more detail before reopening – Nicholas Tower Jan 16 '21 at 15:59

1 Answers1

1

The react approach to this will be something like the following:

const cities = [ 'New York City', 'Boston', 'Seattle', 'Miami' ];

const Example = () => {
  const [searchValue, setSearchValue] = useState('');
  const searchRegEx = new RegExp(searchValue, 'gi');
  const filteredCities = cities.filter(city => city.match(searchRegEx));

  return (
    <div>
      <input
        type="text" 
        value={searchValue} 
        onChange={e => setSearchValue(e.currentTarget.value)}
      />
      {filteredCities.map(city => {
        const match = city.match(searchRegexEx);
        const index = match.index;
        return (
          <React.Fragment>
            {city.slice(0, index)}
            <span class="highlight">{city.slice(index, index + searchValue.length)}</span>
            {city.slice(index + searchValue.length)}
          </React.Fragment>
        )
      })}
    </div>
  )
}

I've probably got some off-by-one errors in the above code, so you may need to adjust it. But the basic idea is: split the string into 3 strings, then using JSX, render the second of those strings wrapped in a <span>

Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98