67 lines
2.0 KiB
JavaScript
67 lines
2.0 KiB
JavaScript
import React from 'react';
|
|
import ReactDOM from 'react-dom';
|
|
|
|
class ReactRendererException extends Error {
|
|
constructor(message) {
|
|
super(`ReactRendererException: ${message}`);
|
|
Error.captureStackTrace(this, ReactRendererException);
|
|
}
|
|
}
|
|
|
|
export class ReactRenderer {
|
|
constructor({ component, selector, componentName, props = {} }) {
|
|
Object.assign(this, {
|
|
component,
|
|
selector,
|
|
componentName,
|
|
props,
|
|
});
|
|
this.handleArgumentErrors();
|
|
this.targetElement = this.getTargetElement();
|
|
this.renderComponent();
|
|
}
|
|
|
|
handleArgumentErrors() {
|
|
if (this.component === null) {
|
|
throw new ReactRendererException(
|
|
`Component ${this.componentName} is not defined. Make sure you're ` +
|
|
`using a non-default export statement for the ${this.componentName} ` +
|
|
`class, that ${this.componentName} has an entry point defined ` +
|
|
'within the \'entry\' section of webpack.common.config.js, and that the ' +
|
|
'entry point is pointing at the correct file path.',
|
|
);
|
|
}
|
|
if (!(this.props instanceof Object && this.props.constructor === Object)) {
|
|
let propsType = typeof this.props;
|
|
if (Array.isArray(this.props)) {
|
|
propsType = 'array';
|
|
} else if (this.props === null) {
|
|
propsType = 'null';
|
|
}
|
|
throw new ReactRendererException(
|
|
`Invalid props passed to component ${this.componentName}. Expected ` +
|
|
`an object, but received a ${propsType}.`,
|
|
);
|
|
}
|
|
}
|
|
|
|
getTargetElement() {
|
|
const elementList = document.querySelectorAll(this.selector);
|
|
if (elementList.length !== 1) {
|
|
throw new ReactRendererException(
|
|
`Expected 1 element match for selector "${this.selector}" ` +
|
|
`but received ${elementList.length} matches.`,
|
|
);
|
|
} else {
|
|
return elementList[0];
|
|
}
|
|
}
|
|
|
|
renderComponent() {
|
|
ReactDOM.render(
|
|
React.createElement(this.component, this.props, null),
|
|
this.targetElement,
|
|
);
|
|
}
|
|
}
|