【问题标题】:How to render child component in the same tree如何在同一棵树中渲染子组件
【发布时间】:2016-04-19 13:14:35
【问题描述】:

这可能是我的一个误解,但我有以下情况:

我有一个名为 DaliBox 的 React 组件,它具有一些智能(拖动、调整大小等),其中可能包含一些我不知道的 HTML,并使用 __dangerouslySetInnerHTML 分配给 DaliBox。但是,如果该 HTML 中有一个自定义标签(在本例中为 <plugin />),那么我希望它托管一个新的 React 组件(PluginPlaceholder),该组件将充当 DaliBox 容器。

我所做的是,在 ComponentDidMount 内部,在 DOMNNode 中找到 <plugin /> 标记,然后使用 ReactDOM.render 渲染传递必要道具的 PluginPlaceholder。问题是,当使用 Chrome 的 React DevTools 扩展时,我可以看到这会创建某种不同的“树”,迫使我手动更新内容。

问题是,我做错了吗?这是唯一的方法还是可以在旧的 React 树中创建新的 React 树并让 React 发挥它的魔力并自动更新?

非常感谢您

【问题讨论】:

  • 请编码 sn-ps 否则它永远不会发生

标签: javascript reactjs render children


【解决方案1】:

您应该尽可能避免使用__dangerouslySetInnerHTML。对于您的情况(如果我没记错的话),您不需要。

让我们考虑<DaliBox /> 和标记<div><p><PluginPlaceholder /></p></div>。从您的问题中,我了解到您具有以某种方式解析此标记的功能。所以让我们考虑函数parseMarkup,它返回类似

[
  component: 'div',
  children: [
    component: 'p',
    children: [
      component: 'PluginPlaceholder'
    ]
  ]
]

不,你可以在没有任何魔法的情况下渲染你的树,比如:

renderChildren(markup) {
  let component;
  let children = [];
  // get component. PluginPlaceholder or just string like 'div' or 'p'
  switch (markup.component) {
    case 'PluginPlaceholder':
      component = PluginPlaceholder;
      break;
    default:
      component = markup.component;
  }
  // recursively render children
  if (markup.children) {
    children = this.renderChildren(markup.children);
  }
  return React.createElement(component, [], children);
}

render() {
  return (
    <DaliBox>
      {this.renderChildren(parseMarkup(this.props.markup))}
    </DaliBox>
  );
}

我不测试此代码,它可能无法正常工作,但我希望您能理解并针对您的问题扩展它

【讨论】:

    【解决方案2】:

    根据您的描述,您的 DOM 树似乎有多个所有者/编辑者:

    • &lt;body&gt;
      • ... 其他 HTML 元素 --> 不由 react 管理
      • &lt;DaliBox /&gt; --> 由 React 管理
        • ...其他 HTML --> 不由 react 管理
        • &lt;div id='pluginplaceholder'&gt; --> 不是由 react 管理,而是用新的(递归)react &lt;DaliBox&gt; 替换
          • ...等等

    这对于反应来说并不理想(委婉地说)。感觉就像您正在尝试对 DOM 结构中的随机级别应用响应。
    这是 jQuery 允许和促进的,但它不适合 react。

    按照最好到最坏的方式来应用反应:

    • 应用 react 的理想方式是让 react 完全垄断 DOM 结构的 1 部分,包括所有后代。然后你有一个反应实例,管理你的 DOM/HTML 的特定(如果不是全部)。
    • 不太理想,但仍然很常见的做法:将 react 应用于 DOM 结构的多个不相关部分。通过拥有多个反应实例(多个ReactDOM.render()s)。
    • 甚至更不理想,但如果绝对不可避免的话也可以接受:让 react 完全垄断 DOM 结构的 1 个(或多个)部分,但忽略最低级别。通过将_dangerouslySetInnerHTML() 应用于这些最低级别。
    • 不惜一切代价避免灾难的秘诀:在树中使用 react 管理 DOM 级别 1-4,但不管理级别 5 和 6,并使用另一个 react 实例管理 DOM 级别 7 及更低级别。

    在您的递归情况下,我建议坚持理想情况:根本不要使用_dangerouslySetInnerHTML()。 或者重新考虑你的代码/行为/DOM 的特定部分,react 是适用的。 (也许您不需要在 DOM 的拖放部分做出反应)。

    这背后的原因:react 依赖于它自己的(隐藏的)DOM 虚拟副本并依赖于虚拟副本和 DOM 始终保持同步。如果其他一些代码开始弄乱应该管理的 DOM 部分,那么您的虚拟 DOM 和真实 DOM 将不再同步,并且您的 React 代码将很快产生不可预测的结果,并且变得完全不可调试。

    【讨论】:

    • 我知道我所拥有的不是最佳的,但我想不出更好的东西。我在一个巨大的项目中工作,同时有很多东西在移动。无论如何,感谢您的全面回复:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-19
    • 2020-01-31
    • 2012-06-25
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多