Home > Mobile >  React wrap a word inside a React component
React wrap a word inside a React component

Time:09-03

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

CodePudding user response:

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!

CodePudding user response:

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>;
}

CodePudding user response:

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
})
  • Related