Martin Mazza Dawson Martin Mazza Dawson - 3 months ago 18
HTML Question

ReactJs dynamic html, binding values to the child class

I'm trying to create a plugin for an audio player where the user can specify their own optional html. The html that the user does specify should have certain properties that are defined in the player file.

At the moment in the plugin, the render method looks like this (cut for brevity example):

player.js:

render(){
return (
<div id="player">
<a class="jp-play" style={this.state.playStyle} onClick={this.play}>
<i class="fa fa-play"></i>
</a>
<div class="jp-current-time">{this.state.currentTime}</div>
</div>
);


Instead of hardcoding the JSX html in the plugin render (player.js) method I want to do something like this:

render(){
return (
{this.props.playerHtml}
);


where the parent calls this like so:

app.js:

render(
<Player playerHtml={getHtml()} />
, document.getElementById("app"));

function getHtml(){
<div id="player">
<a class="jp-play" style={this.state.playStyle} onClick={this.play}>
<i class="fa fa-play"></i>
</a>
<div class="jp-current-time">{this.state.currentTime}</div>
</div>
}


So the user can specify their own html for the plugin. This way instad of passing a
fa-play
icon within
jp-play
they can pass whatever they want.

The problem is that the onClick event handler and states passed through will no longer work properly as
this.state.playStyle
and
this.state.currentTime
will point to app.js file and not player.js.

My question is, how do I allow the user to supply their own html like this but bind the values of the html the user passes and the events to values and functions in my
player.js
? I could do this easily by modifying the dom but I don't think this is the react way.

Answer

You can pass the function to generate the player elements in a prop. The important thing is that it has to be binded to the Player component instance before being called.

Here there is a working version of your code. Take into account that you are returning a JSX element inside getHtml, not pure HTML, so be sure to change class for className and so on.

Important: I hope you understand the security implications of what you are trying to do.

class Player extends React.Component {
  constructor(props) {
    super();
    this.state = {
      playStyle: { background: 'red' },
      currentTime: new Date()
    }
  }
  
  render() {   
    return (
      <div>
       Player:
       { this.props.playerHtml.bind(this)() }
      </div>
    );
  }
};


function getHtml() {
  return (
    <div id="player">
        <a className="jp-play" style={this.state.playStyle} onClick={this.play}>
            <i className="fa fa-play">Play</i>
        </a>
        <div className="jp-current-time">{this.state.currentTime.toString()}</div>
    </div>
  );
}

ReactDOM.render(
  <Player playerHtml={getHtml} />
  , document.getElementById("app")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="app"></div>

Comments