1

I am trying to add a button tag between a text when a pattern is found in react, this is done inside the dangerouslySetInnerHTML attribute. The onClick only works with alert() and console.log(), it doesn't work when I passed a function to it. What am I missing?

    export const detectHashTagPattern = (text) => {
      if(!text) return '';
      let pattern = /This is a follow-up to your previous request #[0-9]+/gi;
      let hashTagPattern = /#[0-9]+/;
    
      text = text.replace(pattern, (res) => {
        return res.replace(hashTagPattern, `<button onClick={}>${hashTagPattern.exec(res)}</button>`);
      });

  return text;
};
  • Regarding your question: Does the function exist in the `window` object? But a better solution might be to split up the string into an array, if you've found your pattern. Then render it by iterating through it. By having an array, you prevent using `dangerouslySetInnerHTML` altogether. – Felix Mar 28 '21 at 09:13
  • The function already exists. The approach of not using dangerouslySetInnerHTML only renders the component with I want to invoke the method from as [object Object] – Chris Okebata Mar 28 '21 at 10:47
  • So the goal is when a particular pattern is detected, the pattern text is converted to a button component which when clicked, a modal is opened to display some information – Chris Okebata Mar 28 '21 at 11:01

1 Answers1

0

Well you could change the algorithm like the following:


function clickHandler() {
  console.log("do something");
}

window.clickHandler = clickHandler;

export const detectHashTagPattern = (text) => {
  if (!text) return "";
  let pattern = /This is a follow-up to your previous request #[0-9]+/gi;
  let hashTagPattern = /#[0-9]+/;

  text = text.replace(pattern, (res) => {
    return res.replace(
      hashTagPattern,
      // use window.<fct>()
      `<button onClick="window.${window.clickHandler.name}()">${hashTagPattern.exec(
        res
      )}</button>`
    );
  });

  return text;
};

You shouldn't go with this approach, as it might have other issues with the rendering in your application (depends what you're doing in the handler).

A better approach would be like this:

const splitSubject = (text) => {
  if (!text) {
    return [""];
  }

  let pattern = /This is a follow-up to your previous request #[0-9]+/gi;

  if (!pattern.test(text)) {
    return [text];
  }
  let hashTagPattern = /#[0-9]+/;
  let result = text.search(hashTagPattern);
  return [text.slice(0, result), text.slice(result), text.slice(result + 1)];
};

const Subject = ({ subject, handler }) => {
  const splitted = splitSubject(subject);

  let content = null;
  if (splitted.length === 1) {
    content = <span>{splitted[0]}</span>;
  } else {
    let [info, cmd, reqReference] = splitted;

    content = (
      <>
        <span>{info}</span>
        <button onClick={() => handler?.(reqReference)}>{cmd}</button>
      </>
    );
  }

  return <p>{content}</p>;
};

export default function App() {
  const requestHandler = (num) => {
    console.log(`click on '${num}'`);
  };

  return (
    <div>
      <Subject
        handler={requestHandler}
        subject="This is a follow-up to your previous request #9"
      />
      <Subject handler={requestHandler} subject="Not matching subject" />
    </div>
  );
}


Felix
  • 2,531
  • 14
  • 25
  • The first approach throws an Uncaught ReferenceError: clickHandler is not defined at HTMLButtonElement.onclick. However, for the second approach, the following text after the detected pattern appears as a button as well – Chris Okebata Mar 28 '21 at 12:10
  • The first approach works now, thanks a lot. How would you recommend I passed a parameter to clickHandler() if I need to and also go about invoking it within the onClick? – Chris Okebata Mar 28 '21 at 12:41
  • See approach 2 for that. The handler works exactly like that. You might also check out https://stackoverflow.com/questions/10000083/javascript-event-handler-with-parameters – Felix Mar 28 '21 at 12:45
  • So, I tried extending the first approach by passing a function as a parameter so it can be called from any other file export const detectHashTagPattern = (text, handleEvent) => { if (!text) return ""; let pattern = /This is a follow-up to your previous request #[0-9]+/gi; let hashTagPattern = /#[0-9]+/; text = text.replace(pattern, (res) => { return res.replace( hashTagPattern, ``... I get an Uncaught SyntaxError: Unexpected token ')' – Chris Okebata Mar 28 '21 at 14:03