如何判断哪个组件生成了DOM节点?

时间:2015-12-31 18:07:55

标签: reactjs

有没有办法告诉哪个组件生成了特定的DOM节点?例如

CustomDiv

<div />只是一个生成<div data-reactid=".0.0.$/=11">a</div> <div data-reactid=".0.0.$/=12">b</div> <div data-reactid=".0.0.$/=13">c</div> 元素的包装器。

在DOM中,这些表示为:

CustomDiv

有没有办法告诉DecoratorComponent组件生成了哪些DOM节点?

上下文:

我有render包装它装饰的组件的DecoratorComponent方法。 let Foo; Foo = class extends React.Component { render () { return <div> <SomeOtherComponent /> {['a', 'b', 'c'].map((letter) => { return <p>{letter}</p> })} </div>; } }; Foo = DecoratorComponent(Foo); 然后修改生成的DOM。

DecoratorComponent

但是,SomeOtherComponent必须仅修改目标组件生成的DOM,即它应排除{['a', 'b', 'c'].map((letter) => { return <p>{letter}</p> })}的输出。

我需要找到一种方法来区分在组件中动态生成的DOM(本例中为Foo)和由doInBackground组件中的另一个组件生成的DOM。

4 个答案:

答案 0 :(得分:5)

您可以使用React Developer Tools检查哪些DOM节点已由哪个React组件呈现。

此外,请查看./src/renderers/dom/client/ReactMount.js,这是用于管理DOM节点及其与React组件的关系的<C> <A>。请注意,react-dom属性在源代码中称为Object

答案 1 :(得分:0)

我建议你不要直接操纵DOM。可以通过一些预防措施,但它可能会降低性能,并可能引入难以发现的错误。让我提出一些想法来解决你在反应空间中的任务。

让我们从最简单的开始吧。我猜你试过这样的事情:

const Foo = (props) => ( 
  <div>
    <SomeOtherComponent />
    {['a', 'b', 'c'].map((letter) => { return <Decorator><p>{letter}</p></Decorator> })}
  </div>
);

如果你需要一次操作列表元素 - 你可以用这种方式包装它们:

const Foo = (props) => ( 
  <Decorator>
    <SomeOtherComponent />
    {['a', 'b', 'c'].map((letter) => { return <p>{letter}</p> })}
  </Decorator>
);

装饰器组件可以访问子反应元素。例如,我们可以将除SomeOtherComponent

的实例之外的所有内容都加粗
const Decorator = (props) => (
  <div>
    { React.Children.toArray(props.children).map(child => 
        child.type === SomeOtherComponent ? child : <b key={child.key}>{child}</b>
    )}
  </div>  
);

如果你想进一步决定在靠近应用程序根目录的地方使用哪个装饰器,你可以简单地将装饰器组件作为道具传递

<Foo decorator={Bolderator}/>

const Foo = ({decorator: Decorator}) => (
  <Decorator>
    <SomeOtherComponent />
    {['a', 'b', 'c'].map((letter) => { return <p>{letter}</p> })}
  </Decorator>
);

答案 2 :(得分:0)

关于哪个组件通过挖掘React组件实例内部结构来呈现哪些可以找到的数据,但是你说你想要一个DOM节点并找出哪个React组件呈现它,这个有点难。

你真的需要在 React刷新之后操作DOM ,或者你满足于修改React元素(从JSX / React.createElement调用返回)呈现给DOM之前发生了什么?无论哪种方式,拦截render自己允许您检查元素并用它做你想做的事情。由于子复合组件不会在以后扩展到真正的DOM节点,因此您只能得到您关心的组件。

这是一个装饰器的示例,它将为给定类呈现的每个元素添加一个属性:

function recursiveAddProps(elem, extraProps) {
  if (!elem) return;
  if (typeof elem === "string") {
    // strings get turned into spans
    return React.createElement("span", extraProps, elem);
  }

  const props = elem.props || {};
  const newChildren = React.Children.map(props.children, child =>
    recursiveAddProps(child, extraProps)
  )
  return React.cloneElement(elem, extraProps, newChildren);
}

function DecoratorComponent(klass) {
  const oldRender = klass.prototype.render;
  klass.prototype.render = function() {
    const elem = oldRender.apply(this, arguments);
    const name = this.constructor.displayName || this.constructor.name;
    return recursiveAddProps(elem, {
      className: "myOverriddenClass" // this will set className on every element
    });
  };

  return klass;
}

使用这些组件

class Foo extends React.Component {
  render() {
    return (
      <div>
         <div>Hi!</div>
        <SomeOtherComponent />
        {['a', 'b', 'c'].map(letter => <div>{letter}</div> )}
      </div>
    );
  }
}

Foo = DecoratorComponent(Foo);

class SomeOtherComponent extends React.Component {
  render() {
    return (
      <ul>
        <li>Some</li>
        <li>Other</li>
        <li>Component</li>
      </ul>
    );
  }
}

代码导致DOM:

<div class="myOverriddenClass" data-reactid=".0">
  <div class="myOverriddenClass" data-reactid=".0.$/=10">
    <span class="myOverriddenClass" data-reactid=".0.$/=10.$/=10">Hi!</span>
  </div>
  <ul data-reactid=".0.$/=11">
    <li data-reactid=".0.$/=11.0">Some</li>
    <li data-reactid=".0.$/=11.1">Other</li>
    <li data-reactid=".0.$/=11.2">Component</li>
  </ul>
  <div class="myOverriddenClass" data-reactid=".0.$/=12=20">
    <span class="myOverriddenClass" data-reactid=".0.$/=12=20.$/=10">a</span>
  </div>
  <div class="myOverriddenClass" data-reactid=".0.$/=12=21">
    <span class="myOverriddenClass" data-reactid=".0.$/=12=21.$/=10">b</span>
  </div>
  <div class="myOverriddenClass" data-reactid=".0.$/=12=22">
    <span class="myOverriddenClass" data-reactid=".0.$/=12=22.$/=10">c</span>
  </div>
</div>

您可以看到该类已添加到的每个元素中,除了SomeOtherComponent呈现的那些元素 - ulli

如果您想在DOM节点上设置一些特殊属性,以便您可以识别Foo组件是否呈现它,您可以指定data-属性,React会将其刷新到DOM。

<div data-rendered-by="Foo" data-reactid=".0">
  <div data-rendered-by="Foo" data-reactid=".0.$/=10">
    <span data-rendered-by="Foo" data-reactid=".0.$/=10.$/=10">Hi!</span>
  </div>
  <ul data-reactid=".0.$/=11">
    <li data-reactid=".0.$/=11.0">Some</li>
    <li data-reactid=".0.$/=11.1">Other</li>
    <li data-reactid=".0.$/=11.2">Component</li>
  </ul>
  <div data-rendered-by="Foo" data-reactid=".0.$/=12=20">
    <span data-rendered-by="Foo" data-reactid=".0.$/=12=20.$/=10">a</span>
  </div>
  <div data-rendered-by="Foo" data-reactid=".0.$/=12=21">
    <span data-rendered-by="Foo" data-reactid=".0.$/=12=21.$/=10">b</span>
  </div>
  <div data-rendered-by="Foo" data-reactid=".0.$/=12=22">
    <span data-rendered-by="Foo" data-reactid=".0.$/=12=22.$/=10">c</span>
  </div>
</div>

此处每个DOM节点都有一个data-rendered-by="Foo"属性,允许您单独告诉DOM该节点是由Foo呈现的。

JSBin上的

Here's a working examplehttps://jsbin.com/tuliqu/edit?js,output

这是一个非常强大的抽象,可能允许你在不触及DOM的情况下做你想做的事情;例如,在React CSS Modules中,没有触及DOM - the components are simply extended and render is overridden

答案 3 :(得分:0)

你的问题似乎让某些术语感到困惑,所以我首先要澄清我是如何理解它的。

  

我有DecoratorComponent包装它装饰的组件的render方法。 DecoratorComponent然后修改生成的DOM。

直接装饰render方法和&#34;修改生成的DOM&#34;是两件非常不同的事情。 render方法不会返回DOM片段,它会生成ReactElement。确定ReactElement表示DOM元素或React组件是否(在大多数情况下)非常简单 - 在前一种情况下typeString,而在其他情况下({{1}在后者中。}或Function)。

Class

但是,如果您没有直接装饰function decorateRender(render) { const el = render(); el.props.children = React.Children.map( el.props.children, (child, idx) => (typeof child.type === 'string') ? <div className="wrapper">{child}</div> : child ); return el; } 并且您确实在尝试修改生成的已装载DOM,则默认情况下无法将DOM元素链接回其生产者。在开发环境中,您可以尝试h4ck React内部并找到链接(请参阅React Developer Tools),但在生产环境中没有任何反向链接。在React&#34;虚拟DOM&#34;中,DOM只是一种产品,之前就是神奇的事物。当然,您可以随时扩展render以在渲染时使用自定义数据属性标记元素。