4

I have the following use case.

Some HTML from a third party source is loaded inside my React component:

class MyComponent extends Component {
  render() {
    return (
      <div
        dangerouslySetInnerHTML={{ __html: this.props.externalHTML }}
      />
    );
  }
}

Inside the externally loaded HTML a click event exists for a specific span, which is supposed to call a callback function that exists in my application.

<span onclick="myCallback(param1='asd', param2=123, param3='asdas')">
  Click me!
</span>

Where should I put this myCallback function?

If I place it inside the component class I get the following error when clicking the span, because as I understand the function is not visible to the externally loaded HTML: Uncaught ReferenceError: myCallback is not defined at HTMLSpanElement.onclick

My other idea was to add the function to the window object window.myCallback = ... inside my main index.js file to be loaded every time the app loads. This way it works but I have two issues.

  • My understanding is that this is not the correct React way to do it.
  • Whenever I click the span element the callback function is triggered twice and I cannot understand why.

Any suggestions?

1 Answer 1

1

Using "dangerouslySetInnerHTML" is ..."dangerous" as its name ^^, which is actually not pure React way, either.

However, If you have to do it, you can do something like this (take advantage of built-in jQuery inside React be default)

=====

EDITED VERSION FROM HERE: (use only 1 component)

export default class MyComponent extends Component {
  componentDidMount() {
    // using jQuery to manipulate DOM element form third-party source
    // NB. you may think of using setTimeout here, to wait for the external source to be fully loaded here, of course it's not so safe
    // but anyway, we are using "dangerouslySetInnerHTML" anyway => quite dangerous, though ^^  

    // setTimeout(function(){   
      $(document.findElementsByTagName("span")[0]).click(function(e){ 
      // or perhaps $("#spanID").click if you can, just be careful between DOM element and react component

          e.preventDefault();
          // DO SOMETHING HERE, just like you do in the window.onload function
          // or maybe you also need to get param values by getting $(this).data("...") or $(this).attr("ATTRIBUTE-NAME")
          return false;

      });
    // });
  }
  render() {
    return (
      <div
        dangerouslySetInnerHTML={{ __html: this.props.externalHTML }}
      />
    );
  }
}   

=====

OLD ANSWER FROM HERE: (use 2 components)

ParentComponent:

export default class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.callbackOnThisComponent = this.callbackOnThisComponent.bind(this);
  }
  callbackOnThisComponent(param1, param2, param3) {
    // do whatever you like with the above params
  }
  render() {
    return (
      <ChildComponent triggerCallbackOnParent={this.callbackOnThisComponent} />
    );
  }
}

ChildComponent:

export default class ChildComponent extends Component {
  componentDidMount() {
    // using jQuery to manipulate DOM element form third-party source

    let that = this;

    // NB. you may think of using setTimeout here, to wait for the external source to be fully loaded here, of course it's not so safe
    // but anyway, we are using "dangerouslySetInnerHTML" anyway => quite dangerous, though ^^  

    $(document.findElementsByTagName("span")[0]).click(function(e){ 
    // or perhaps $("#spanID").click if you can, just be careful between DOM element and react component

        e.preventDefault();
        that.props.triggerCallbackOnParent(param1, param2, param3);
        // or maybe you need to get param values by getting $(this).data("...") or $(this).attr("ATTRIBUTE-NAME")
        return false;

    }, that);

  }
  render() {
    return (
      <div
        dangerouslySetInnerHTML={{ __html: this.props.externalHTML }}
      />
    );
  }
}   

I just use the main React's idea, which is passing props downward to children components, and when you want to trigger a function upward from child component, create a callback function on parent. For your or anyone else's reference, this is my demonstration on how to pass callback function from parent to multi-level-children components:

Force React container to refresh data

Re-initializing class on redirect

if this doesn't work yet, feel free to show me some error logs, thanks

Sign up to request clarification or add additional context in comments.

2 Comments

I don't have another way to do this than using dangerouslySetInnerHTML. I know that it is dangerous. :) But I do not want to use jquery either. Is this the only way?
Actually, third-party source is just HTML, not react-component, and may not have event bound to it yet, unless we use javascript to bind the events later. Therefore, if you don't wanna use jQuery, you may use "pure" javascript, I think: something like "document.getElementById("spanID").addEventListener("click", function(){})" ^^

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.