在不使用邪恶eval的情况下动态呈现React组件

时间:2017-03-07 14:53:51

标签: javascript reactjs eval

我有一个工作解决方案(如下所示),用于在我无法使用标记中的数据属性显式加载它的情况下动态加载我的React组件。我意识到我使用'eval'从组件注册表中获取动态组件,我正在寻找更好的解决方案。

export default class DynamicComponentRenderer {

/**
 * Creates an instance of DynamicComponentRenderer.
 * 
 * @param {object} element The DOM element to make into a component.
 * 
 * @memberOf DynamicComponentRenderer
 */
constructor(element, componentRegistry) {
    Guard.throwIf(element, "element");
    Guard.throwIf(componentRegistry, "componentRegistry");

    this.element = element;
    this.componentName = element.getAttribute("data-react-component");
    this.DynamicComponent = eval(`componentRegistry.${this.componentName}`);
    this.props = {};

    if (!this.DynamicComponent) {
        throw new Error(`Unable to create a component of the name '${this.componentName}'.`)
    }

    Array.prototype.slice.call(element.attributes).filter((attrib) => 
        (attrib.name.includes('data-') && !attrib.name.includes('data-react-component'))
    ).forEach((attrib) => {
        this.props[attrib.name.replace(/data-/i, '').replace(/-[a-z]/, (match) => {
            return match.toUpperCase();
        }).replace(/-/, '')] = attrib.value;
    });
}

/**
 * Renders the dynamic React component.
 * 
 * @returns Rendered HTML.
 * 
 * @memberOf DynamicComponentRenderer
 */
render() {
    ReactDOM.render(React.createElement(this.DynamicComponent, this.props), this.element);
}
}

传入的componentRegistry来自一个看起来有点像这样的组件索引文件......

export default {
    DataList: DataList,
    Form: {
        Buttons : {
             Submit: SubmitButton,
             Cancel: CancelButton
        }
    }
    OwnedAddress: OwnedAddress
}

DynamicComponentRegister由JsComponentManager加载......

import PathManager from "../../SharedJs/path-manager";
import AppComponents from "./components/index.jsx";
import Dynamic from "../../SharedJs/components/dynamic-component-renderer";

export default class JsComponentManager {

    constructor(onLoader, pathManager) {
        this.loader = onLoader;
        this.pathManager = pathManager;
        this.select = {
            reactComponents: () => $(".js-react-component")
        }
    }

    bindComponents() {
        const paths = new PathManager();
        let $reactComponents = this.select.reactComponents()
        if ($reactComponents.length > 0) {
            this.loader.add(this.renderReactComponents, $reactComponents);
        }
    }

    renderReactComponents($elements) {
        $.makeArray($elements).forEach((el) => {
            let dynamicRenderer = new Dynamic(el, AppComponents);
            document.DynamicRenderers = document.DynamicRenderers || [];
            document.DynamicRenderers.push(dynamicRenderer);
            dynamicRenderer.render();
        });
    }
}

PathManager在当前实例中没有做任何事情(它允许我通过URL查询URL和呈现组件,但我现在不这样做)。传入构造函数的'onLoader'将onLoad事件的事件排队。

1 个答案:

答案 0 :(得分:1)

如果您只是使用eval来解析某些对象的路径,则可以改为使用一个用于遍历对象的函数。

const traverseInternal = (object, keys, keyIndex) => {
  if (keyIndex >= keys.length) {
    return object;
  }

  return traverseInternal(object[keys[keyIndex]], keys, keyIndex + 1);
};

const traverse = (object, deepKey) => {
  return traverseInternal(object, deepKey.split('.'), 0);
};

然后替换

eval(`componentRegistry.${this.componentName}`);

traverse(componentRegistry, this.componentName);