在React组件中渲染或使用DocumentFragment

时间:2018-04-17 04:29:22

标签: javascript reactjs

我在基于React的应用程序中要求渲染动态表单。表单定义存储为JSON文档,我已经有一个JS库,它解析定义并返回DocumentFragment。此库也用于其他非React应用程序,因此我无法更改它。

为了避免在我的React应用程序中重写整个逻辑来解析定义并呈现表单,我想使用现有的库。

我的问题是,在React组件中呈现DocumentFragment的最佳方法是什么?

如果我只是在render()方法中将它输出到控制台,那么这是我的DocumentFragment。

#document-fragment
    <fieldset id="metadata-form-908272" class="metadata-form-rendition hide-pages">
      <div class="page-header-row">
        <div class="page-header-cell">
          <span>[Un-named page]</span>
          <button class="page-header-button button icon">
            <span class="icon icon-arrow-up-11"></span></button>
        </div>
      </div>
      <div class="page-area" id="metadata-form-page-001-area">
        <div class="question-row">
          <div class="question-label-cell mandatory">I have read and understood my obligations:</div>
          <div class="question-input-cell">
            <div class="validation-message"></div>
            <label><input type="radio" value="Yes" name="metadata-form-908272-question-1">
              <span>Yes</span></label>
            <label><input type="radio" value="No" name="metadata-form-908272-question-1">
              <span>No</span></label>
          </div>
        </div>
        <div class="question-row">
          <div class="question-label-cell">Please state all sources for the information provided:</div>
          <div class="question-input-cell">
            <div class="validation-message"></div>
            <div class="formatted-editor">
              <div class="editor-area" contenteditable="true">
                <p>​</p>
              </div>
            </div>
          </div>
        </div>
      </div>
    </fieldset>

2 个答案:

答案 0 :(得分:2)

更新:安全警报!

感谢@JaredSmith在评论中提醒,这里提供的方法 确实有安全问题。如果库不是来自您的内部,则应用它是不合适的。

要了解有关此问题的更多信息,您可以查看我在下面提到的dangerouslysetinnerhtml的链接。

这确实是实现目标的棘手方法。根据您在评论中提供的信息:

  

...我调用了ExternalLibrary.getFormFragment({some_data})......

导致DocumentFragments仅在内存中,或许你知道,我们需要先将片段附加到真正的DOM元素,所以让我们创建一个根元素来追加:

let rootElement = document.createElement("div");
let frag = theExternalLibrary.getFormFragment({some_data});
rootElement.appendChild(frag);

现在我们在这里有一个纯JavaScript元素DOM树。为了将其转换为React元素,以下是涉及提供反应的方法的方式:dangerouslysetinnerhtml
你可以看到不鼓励这个方法使用它可怕的名字。

render() {
  let rootElement = document.createElement("div");
  let frag = theExternalLibrary.getFormFragment({some_data});
  rootElement.appendChild(frag);
  // rootElement.innerHTML is in string type.
  return <div dangerouslySetInnerHTML={{ __html: rootElement.innerHTML }} />;
}

实例:

Edit DocumentFragment

答案 1 :(得分:0)

我本人对此表示怀疑,因为当返回DocumentFragments时,DOMPurify和即将推出的Sanitiser API效果最好:

(data as NSString).length
function getDocumentFragment(text) {
  const f = document.createDocumentFragment();
  const p = document.createElement("p");
  p.textContent = text;
  f.appendChild(p);
  return f;
}

class FragmentRenderer extends React.Component {
  constructor(props) {
    super(props);

    this.setRef = this.setRef.bind(this);
  }

  setRef(ref) {
    this.ref = ref;
  }

  componentDidMount() {
    this.appendFragment();
  }

  componentDidUpdate(prevProps) {
    if (prevProps.text != this.props.text) {
      this.appendFragment();
    }
  }

  appendFragment() {
    if (!this.ref) {
      return;
    }
    while (this.ref.firstChild) {
      this.ref.removeChild(this.ref.firstChild);
    }
    this.ref.appendChild(getDocumentFragment(this.props.text));
  }

  render() {
    return React.createElement("div", {
      ref: this.setRef
    });
  }
}

ReactDOM.render(React.createElement(FragmentRenderer, {
  text: "Just like this"
}), document.getElementById("app"));

因此,现在您不必转换为字符串,而不会使用angerousedSetInnerHTML,在极端情况下,由于解析问题,这可能导致您编写的元素与原始片段中的元素不同。但是,无论此片段来自何处,您仍需要信任-它不能来自用户控制的来源。 DOMPurify或Sanitizer API将是您最好的朋友。