skip to Main Content

I have a text such as

"This is my text Albert is my special word"

also I have a react component called SpecialWord I want to search through the text above and wrap all the special words in this case (Albert) with my SpecialWord Component

so the text above will output to something like this

This is my text <SpecialWord>Albert</SpecialWord> is my special word

after that I want to render the final result as a react component so something like this if possible

<div dangerouslySetInnerHTML={{__html: finalResult}}></div>

I already tried to get the start and end index of the special word and wrapped it with native html elements, when rendered it works fine since they are native html elements but I can’t use the same approach with the SpecialWord component since it’s not a native html elements

3

Answers


  1. Chosen as BEST ANSWER

    I solved the problem by some splitting and recombining if anyone were interested in the solution here's a sandbox https://codesandbox.io/s/amazing-surf-uor8sd?file=/src/pages/index.js

    import { useEffect } from "react";
    import { useState } from "react";
    
    const text = "hello albert is my special word";
    
    export default function Ex() {
      const [result, setResult] = useState([]);
      const [specialWords, setSpecialWords] = useState([
        {
          word: "albert",
          data: {
            explination: "albert is a special word",
          },
        },
      ]);
    
      useEffect(() => {
        const arrayOfWords = text.split(" ");
        let result = arrayOfWords.map((word) => {
          const specialWord = specialWords.find(
            (specialWord) => specialWord.word === word
          );
          if (specialWord) {
            return <SpecialWord data={specialWord} key={specialWord.word} />;
          }
          return word;
        });
    
        // the results array look someting like this:
        // ['word', 'word', <SpecialWord />, 'word', 'word']
        // we need to join as much string as possible to make the result look like this:
        // ['word word', <SpecialWord />, 'word word']
        // this will improve the performance of the rendering
    
        let joinedResult = [];
        let currentString = "";
    
        for (let i = 0; i < result.length; i++) {
          const word = result[i];
    
          if (typeof word === "string") {
            currentString += word + " ";
          } else {
            joinedResult.push(currentString);
            joinedResult.push(word);
            currentString = "";
          }
        }
        joinedResult.push(" " + currentString);
    
        setResult(joinedResult);
      }, []);
    
      return (
        <div>
          {result.map((word) => {
            return word;
          })}
        </div>
      );
    }
    
    function SpecialWord({ data }) {
      return <span style={{ color: "red" }}>{data.word}</span>;
    }
    

  2. I would avoid using dangerouslySetInnerHTML.

    You can do something like:

    function SpecialWord({ children }) {
      return <strong>{children}</strong>;
    }
    
    function Component() {
      const segmenter = new Intl.Segmenter([], {
        granularity: 'word'
      });
      const parts = Array.from(segmenter.segment("This is my text Albert is my special word")).map(part => part.segment);
    
      return parts.map((part, index) => {
        if (part === "Albert") {
          return <SpecialWord key={index}>Albert</SpecialWord>;
        }
    
        return part;
      })
    }
    
    ReactDOM.render(
      <Component />,
      document.getElementById("root")
    );
    <div id="root"></div>
    
    <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>

    A couple of notes:

    • You need a way to split the sentence in an array, so that you can use map over the words of the sentence and output a React component for the special word. I used Intl.Segmenter which is fairly new and might not be supported on older browsers. Also, you might want to match things like "special word". Intl.Segmenter splits in words so you won’t be able to match multiple words.
    • I used key={index}, using an index as key is often a mistake. In this case it works because we don’t have a unique ID for each word.
    • Instead of passing the special word as child, it’s better to use a property for this: <SpecialWord word={part} /> (React children are kinda hard to work with)

    I hope this helps!

    Login or Signup to reply.
  3. You can put your text in an array and use map to return the words wrapped with the correct wrapper, for example:

    const message = 'Hello world' 
    const special = "world"
    
    const result = message.split(" ").map(w =>{
      if(special.includes(w)) return <SpecialWord>Albert</SpecialWord>
      else return w
    })
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search