0

I would like to render a component directly without a Target container, i need to get the content of the component right in the place where i put the generated JS file.

Here is my index.js file, i have removed the "document.getElementById('target-container')" is there a way to render this component without a target container or a way to append a target container without inserting it in the HTML template file.

var React = require('react');
var ReactDOM = require('react-dom');
import axios from 'axios';
import '../node_modules/cleanslate/cleanslate.css';
import './style.scss';

class Widget extends React.Component {
constructor(props){
    super(props);
    this.state = {}
}

componentDidMount() {
    let id = this.props.id;
    axios.get(`https://api.com/${id}`)
    .then((res) => {
        const brand = res.data;
        this.setState({
            rating: brand.rating,
            logo : brand.logo,
            name: brand.name,
            stars: brand.stars,
            url: brand.url
        });
    });
}

render() {
    return (
        <div className="cleanslate">
            <a href={this.state.url} target="_blank">
                <img src="https://img/.svg" className="" alt="" />
                <div className="rating-box">
                    <img src={this.state.logo} className="logo" alt={this.state.name} />
                    <span className="note">{this.state.rating}/10</span>
                    <div id="Selector" className={`selected-${this.state.stars}`}></div>
                </div>
            </a>
        </div>
    )
}
}

ReactDOM.render(
    <Widget id="7182" />
)

Here is an example (https://github.com/seriousben/embeddable-react-widget) of appending the component in another one :

import React from 'react';
import ReactDOM from 'react-dom';
import Widget from '../components/widget';
import '../../vendor/cleanslate.css';

export default class EmbeddableWidget {
static el;

static mount({ parentElement = null, ...props } = {}) {
const component = <Widget {...props} />;

function doRender() {
  if (EmbeddableWidget.el) {
    throw new Error('EmbeddableWidget is already mounted, unmount first');
  }
  const el = document.createElement('div');
  el.setAttribute('class', 'cleanslate');

  if (parentElement) {
    document.querySelector(parentElement).appendChild(el);
  } else {
    document.body.appendChild(el);
  }
  ReactDOM.render(
    component,
    el,
  );
  EmbeddableWidget.el = el;
}
if (document.readyState === 'complete') {
  doRender();
} else {
  window.addEventListener('load', () => {
    doRender();
  });
}
  }

  static unmount() {
    if (!EmbeddableWidget.el) {
      throw new Error('EmbeddableWidget is not mounted, mount first');
    }
    ReactDOM.unmountComponentAtNode(EmbeddableWidget.el);
    EmbeddableWidget.el.parentNode.removeChild(EmbeddableWidget.el);
    EmbeddableWidget.el = null;
  }
}
Elchy
  • 171
  • 5
  • 16

1 Answers1

4

You could generate a container for your app with JS before calling ReactDOM.render (for instance with appendChild as described here) and then call ReactDOM.render passing just generated element as container.

UPD:

Even though it feels strange, you actually can get the script tag of your bundle before ReactDOM.render is called.

Knowing this, you could do something like:

// Create a container dynamically
const appContainer = document.createElement('div');

// Get all <script>s on the page and put them into an array.
const scriptTags = Array.from(document.scripts);

// Filter scripts to find the one we need.
const targetScript = scriptTags.filter(
    scriptTag => scriptTag.src === 'https://example.com/bundle.js'
);

// Uh oh, we got either too many or too few,
// it might be bad, better stop right here.
if (targetScript.length !== 1) {
  return;
}

// Inserts app container before the script.
document.body.insertBefore(appContainer, targetScript[0]);

// Renders things inside the container
ReactDOM.render(
    <MyComponent />,
    appContainer
);
Igor Bykov
  • 2,532
  • 3
  • 11
  • 19