2015-12-31 6 views
7

Есть ли способ определить, какой компонент сгенерировал конкретный узел DOM ?, например.Как узнать, какой компонент сгенерировал узел DOM?

<CustomDiv>a</CustomDiv> 
<div>b</div> 
<div>c</div> 

CustomDiv является просто оболочкой, которая генерирует <div /> элемент.

В DOM, они представлены как:

<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, что облегает render метод компонента, который он декорирует. DecoratorComponent затем изменяет итоговую DOM.

let Foo; 

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

Foo = DecoratorComponent(Foo); 

Однако DecoratorComponent необходимо изменить DOM, только то будет производится целевого компонента, то есть она должна исключать выход SomeOtherComponent.

Мне нужно найти способ различать DOM, который был динамически сгенерирован в компоненте ({['a', 'b', 'c'].map((letter) => { return <p>{letter}</p> })} в этом примере) и DOM, которые сгенерированы другим компонентом в компоненте Foo.

+0

Я не верю, что то, что вы пытаетесь сделать, это то, что поддерживается реакцией. Между узлами, создаваемыми при сопоставлении над '['a', 'b', 'c']' и '', нет различий, кроме метаданных типа 'displayName'. Не могли бы вы объяснить большую проблему, которую вы пытаетесь решить, и, может быть, мы могли бы взломать ее решение так, чтобы реагировать более канонически? –

+0

Могу ли я понять, что у вас разные компоненты, написанные в тех же частях DOM? Можете ли вы немного рассказать о том, почему это происходит? –

ответ

5

Вы можете использовать React Developer Tools для проверки того, какие узлы DOM были визуализированы с помощью компонента React.

Кроме того, изучите ./src/renderers/dom/client/ReactMount.js, что является react-domObject, используемым для управления узлами DOM и их отношением к компонентам React. Обратите внимание, что атрибут data-reactid в исходном коде называется ATTR_NAME.

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> 
); 
+0

Реализация существует в реальной жизни в популярном проекте. Не наблюдается заметных последствий для работы. https://github.com/gajus/react-css-modules – Gajus

0

данные о том, какие компоненты оказываемых какие может быть найден копаться в React компонентов внутренностей экземпляра, но вы сказали, что вы хотите сделать узел DOM и выяснить, какие Реагировать компонент вынес его, и это немного Сильнее.

вам действительно нужно манипулировать DOM после ли Реагировать флеши это, или вы контент для изменения Реагировать элемент (который получает вернулся из 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 - ul и li s.

Если вы хотите установить какой-либо специальный атрибут на узлах 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.

Here's a working example на JSBin: https://jsbin.com/tuliqu/edit?js,output


Это довольно мощная абстракция, которая может позволит вам делать то, что вы хотите, даже не прикасаясь к DOM; например, в React CSS Modules, DOM не затрагивается - the components are simply extended and render is overridden.

0

Ваш вопрос, кажется, путает некоторые термины, поэтому мне сначала нужно уточнить, как я это понимаю.

У меня есть DecoratorComponent, что обертка render метод компонента, который он декорирует. DecoratorComponent затем изменяет итоговую DOM.

Непосредственно украшающий метод render и «изменение итоговой DOM» - это две разные вещи. Метод render не возвращает фрагмент DOM, он производит ReactElement. Решив, что ReactElement обозначает элемент DOM, или компонент React (в большинстве случаев) довольно прост - его type - это String в первом случае и что-то еще (a 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; 
} 

Если, однако, вы не непосредственно декорирования render и вы действительно пытаетесь изменить Результирующие, установленный DOM, там не по умолчанию, никоим образом связать элементы DOM обратно к их производителям. В среде разработки вы можете попробовать h4ck React internals и найти ссылку (см. React Developer Tools), но в производственной среде нет никакой обратной ссылки. DOM - это всего лишь продукт, волшебство которого происходит раньше, в «виртуальном DOM» React. Конечно, вы всегда можете расширить React.Component, чтобы пометить элементы атрибутом пользовательских данных для рендеринга.

Смежные вопросы