如何在同一棵树中渲染子组件
How to render child component in the same tree
这可能是我这边的误会,但我有以下情况:
我有一个名为 DaliBox 的 React 组件,它具有一些智能(拖动、调整大小等),可能包含一些我不知道的 HTML 并使用 __dangerouslySetInnerHTML 分配给 DaliBox。但是,如果在 HTML 中有一个自定义标签(在本例中为 <plugin />
),那么我希望它托管一个新的 React 组件(PluginPlaceholder),它将充当 DaliBox 容器。
我所做的是,在 ComponentDidMount 内部,在 DOMNNode 中找到 <plugin />
标签,然后使用 ReactDOM.render 渲染传递必要道具的 PluginPlaceholder。问题是,当使用 Chrome 的 React DevTools 扩展时,我可以看到这会产生某种不同的 "tree",迫使我手动更新内容。
问题是,我是不是做错了什么?这是唯一的方法还是可以在旧的 React 树中创建新的 React 树并让 React 发挥它的魔力并自动更新?
非常感谢您
您应该尽可能避免 __dangerouslySetInnerHTML
。对于你的情况(如果我做对了)你不需要一个。
让我们考虑 <DaliBox />
和标记 <div><p><PluginPlaceholder /></p></div>
。根据您的问题,我了解到您具有以某种方式解析此标记的功能。因此,让我们考虑 parseMarkup
的函数 returns 类似于
[
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>
);
}
我没有测试这段代码,它可能不会工作,但我希望你能理解并扩展它来解决你的问题
根据您的描述,您的 DOM 树似乎有多个所有者/编辑者:
<body>
- ... 其他 HTML 元素 --> 不受 react
管理
<DaliBox />
--> 由 React 管理
- ...其他 HTML --> 不受反应管理
<div id='pluginplaceholder'>
--> 不由 React 管理,但要用新的(递归)React 替换 <DaliBox>
- ...等等
这不是反应的理想选择(委婉地说)。感觉就像您正在尝试将反应应用于 DOM 结构中的随机级别。
这是 jQuery 允许和促进的事情,但它与 react 不相配。
按照从最好到最差的方式应用 React:
- 应用 React 的理想方式是让 React 完全垄断 你的 DOM 结构的 1 部分,包括所有后代。然后你有一个反应实例,管理一个特定的(如果不是全部)你的 DOM/ HTML.
- 不太理想,但仍然是常见的做法:将反应应用于 DOM 结构的多个不相关部分。通过有多个反应实例(多个
ReactDOM.render()
s)。
- 更不理想,但如果绝对不可避免,也许可以接受:让 React 完全垄断 DOM 结构的 1 个(或更多)部分,但忽略最低级别。通过将
_dangerouslySetInnerHTML()
应用到最低级别。
- 灾难的秘诀,要不惜一切代价避免:让 React 管理 DOM 树中的 1-4 级,但不是 5 级和 6 级,以及另一个要管理的 React 实例 DOM 7 级及以下。
在你的递归情况下,我建议坚持理想情况:根本不使用 _dangerouslySetInnerHTML()
。
或者重新考虑您的代码/行为/DOM 中适用 React 的特定部分。 (也许您不需要在 DOM 的 drag/drop 部分做出反应)。
这背后的原因:React 依赖于它自己的 DOM 的(隐藏的)虚拟副本,并且依赖虚拟副本和 DOM 始终保持同步。如果其他一些代码开始混淆 DOM-react 应该管理的部分,那么你的虚拟 DOM 和真实的 DOM 不再同步,你的 react 代码将很快产生不可预测的结果结果,并变得完全不可调试。
这可能是我这边的误会,但我有以下情况:
我有一个名为 DaliBox 的 React 组件,它具有一些智能(拖动、调整大小等),可能包含一些我不知道的 HTML 并使用 __dangerouslySetInnerHTML 分配给 DaliBox。但是,如果在 HTML 中有一个自定义标签(在本例中为 <plugin />
),那么我希望它托管一个新的 React 组件(PluginPlaceholder),它将充当 DaliBox 容器。
我所做的是,在 ComponentDidMount 内部,在 DOMNNode 中找到 <plugin />
标签,然后使用 ReactDOM.render 渲染传递必要道具的 PluginPlaceholder。问题是,当使用 Chrome 的 React DevTools 扩展时,我可以看到这会产生某种不同的 "tree",迫使我手动更新内容。
问题是,我是不是做错了什么?这是唯一的方法还是可以在旧的 React 树中创建新的 React 树并让 React 发挥它的魔力并自动更新?
非常感谢您
您应该尽可能避免 __dangerouslySetInnerHTML
。对于你的情况(如果我做对了)你不需要一个。
让我们考虑 <DaliBox />
和标记 <div><p><PluginPlaceholder /></p></div>
。根据您的问题,我了解到您具有以某种方式解析此标记的功能。因此,让我们考虑 parseMarkup
的函数 returns 类似于
[
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>
);
}
我没有测试这段代码,它可能不会工作,但我希望你能理解并扩展它来解决你的问题
根据您的描述,您的 DOM 树似乎有多个所有者/编辑者:
<body>
- ... 其他 HTML 元素 --> 不受 react 管理
<DaliBox />
--> 由 React 管理- ...其他 HTML --> 不受反应管理
<div id='pluginplaceholder'>
--> 不由 React 管理,但要用新的(递归)React 替换<DaliBox>
- ...等等
这不是反应的理想选择(委婉地说)。感觉就像您正在尝试将反应应用于 DOM 结构中的随机级别。
这是 jQuery 允许和促进的事情,但它与 react 不相配。
按照从最好到最差的方式应用 React:
- 应用 React 的理想方式是让 React 完全垄断 你的 DOM 结构的 1 部分,包括所有后代。然后你有一个反应实例,管理一个特定的(如果不是全部)你的 DOM/ HTML.
- 不太理想,但仍然是常见的做法:将反应应用于 DOM 结构的多个不相关部分。通过有多个反应实例(多个
ReactDOM.render()
s)。 - 更不理想,但如果绝对不可避免,也许可以接受:让 React 完全垄断 DOM 结构的 1 个(或更多)部分,但忽略最低级别。通过将
_dangerouslySetInnerHTML()
应用到最低级别。 - 灾难的秘诀,要不惜一切代价避免:让 React 管理 DOM 树中的 1-4 级,但不是 5 级和 6 级,以及另一个要管理的 React 实例 DOM 7 级及以下。
在你的递归情况下,我建议坚持理想情况:根本不使用 _dangerouslySetInnerHTML()
。
或者重新考虑您的代码/行为/DOM 中适用 React 的特定部分。 (也许您不需要在 DOM 的 drag/drop 部分做出反应)。
这背后的原因:React 依赖于它自己的 DOM 的(隐藏的)虚拟副本,并且依赖虚拟副本和 DOM 始终保持同步。如果其他一些代码开始混淆 DOM-react 应该管理的部分,那么你的虚拟 DOM 和真实的 DOM 不再同步,你的 react 代码将很快产生不可预测的结果结果,并变得完全不可调试。