2014-08-21 2 views
44

Некоторые плагины JQuery не только добавляют поведение к узлам DOM, но и изменяют их. Например, Bootstrap Switch получаетсяИспользование плагинов JQuery, которые преобразуют DOM в React Components?

<input type="checkbox" name="my-checkbox" checked>

в нечто вроде

<div class="bootstrap-switch bootstrap-switch-wrapper bootstrap-switch-on bootstrap-switch-large bootstrap-switch-animate"> 
    <div class="bootstrap-switch-container"> 
    <span class="bootstrap-switch-handle-on bootstrap-switch-primary">ON</span> 
    <label class="bootstrap-switch-label">&nbsp;</label> 
    <span class="bootstrap-switch-handle-off bootstrap-switch-default">OFF</span> 
    <input type="checkbox" name="download-version" checked="" data-size="large" data-on-text="3" data-off-text="2.0.1"> 
    </div> 
</div> 

с

$("[name='my-checkbox']").bootstrapSwitch();

который не джайв с React:

Uncaught Error: Invariant Violation: findComponentRoot(..., .0): Unable to find 
element. This probably means the DOM was unexpectedly mutated (e.g., by the 
browser), usually due to forgetting a <tbody> when using tables or nesting <p> or 
<a> tags. ...<omitted>...`. 

Существует ли рекомендованная методика включения этих плагинов в компоненты React? Или они принципиально нарушают предположения Реагента и не могут с ним работать?

ответ

57

Нет, реакция будет реагировать (ха-ха) плохо на что-либо, что изменяет его собственную структуру компонента компонента вне реакции. Это то, чего вы никогда не хотите делать. Рекомендуемым решением было бы повторить функциональность того, что вы пытаетесь сделать с jquery или похожим плагином, в ответ.

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

Пример:

var Example = React.createClass({ 
    componentDidMount: function() { 
     var $checkboxContainer = $(this.refs.checkboxContainer.getDOMNode()); 

     var $checkbox = $('<input />').prop('type', 'checkbox'); 

     $checkboxContainer.append($checkbox); 

     $checkbox.bootstrapSwitch({}); 
    }, 
    render: function() { 
     return (
      <div> 
       <div ref="checkboxContainer"></div> 
      </div> 
     ) 
    } 
}); 

Теперь, конечно, вы рендеринг компонента с вложенными делами. Вложенные при установке в dom в первый раз, когда вложенный div получит флажок, добавленный к нему jquery, который затем также выполнит наш плагин jquery.

Этот конкретный примерный компонент имеет мало смысла для него, однако вы можете видеть, как это может интегрироваться в более сложный компонент, но при этом позволяет повторно отображать и реагировать на изменения состояния и т. Д. Вы просто теряете способность напрямую реагировать к событиям/изменять вещи внутри указанного флажка, который в отношении реакции не существует.

Теперь с приведенным выше примером, если у вас должна быть какая-то реакционная логика для добавления/удаления вложенного div, вам придется иметь ту же логику, что и вставляемый div, ответственный за повторную установку флажка и повторное вложение, инициализируя его плагином jquery. Однако поскольку реакция только модифицирует dom, когда это необходимо, этот вставленный dom-контент не будет удален, если вы не сделаете что-то, что модифицирует контейнер div таким образом, чтобы он был удален/повторно отображен в dom. Это означает, что вы все равно можете получить доступ ко всем событиям внутри реакции для этого контейнера div и т. Д.

Вы также можете использовать функцию componentDidMount, чтобы реагировать на привязку событий или обратных вызовов к определенным взаимодействиям в этом флажке. Просто убедитесь, что они правильно отвязали их в componentWillUnmount или везде, где это имеет смысл сделать это в жизненном цикле компонента в вашем конкретном случае.

+0

Благодарим за обстоятельный разговор! – tnunamak

+1

Если родительские реендеры, ребенок будет переигрывать, сдуть манипуляции с JQuery DOM. Возможно, используйте 'componentDidUpdate' вместо или так же как' componentDidMount' –

+2

Отличный ответ! За последние несколько месяцев я очень глубоко вписался в React, и я думаю, что ваша рекомендация «повторить функциональность» правильная. Невероятно, сколько плагинов jQuery React для меня устарело, поскольку реализация одной и той же функции часто короче и проще кода, чем плагин jQuery для настройки. например если я хочу показать поле типа «XXX символов слева», тривиально с состояниями, чтобы положить мой рендер(): '

{this.props.maxChars - this.state.q.length} characters left
' –

20

В учебнике этого великого ryanflorence в вы получите представление о том, как это сделать:

Wrapping DOM Libs

Методология

  1. DOM ЛИЭС обычно манипулировать DOM
  2. Реагировать пытается для повторного рендеринга и поиска другой DOM, чем в прошлый раз, и уродцев
  3. Мы скрываем манипуляции DOM от React, взломав дерево рендеринга, а затем , воссоздавая вокруг DOM, которым манипулирует lib.
  4. Потребители нашего компонентов могут оставаться в зоне реактивов.
+0

Это действительно очень хорошее объяснение. Благодаря! – Kobold

2

Это скорее философский вопрос

React была создана для оптимизации DOM манипуляции и имеет много проводов за кулисами, чтобы сделать это при изменении состояния отображения компонента через SetState

Doing так что эта проводка будет пересекать ее виртуальную DOM, чтобы найти узлы, которые необходимо обновить.

Если вы должны использовать React, чтобы попытаться сохранить уровень согласованности в вашей кодировке, лучше всего применить t он JQuery DOM манипуляции внутри componentDidMount как так ...

componentDidMount(){ 
     this.node = $("#"+this.props.id); // Keep a reference to the node 
     this.chart = this.node.easyPieChart(); // Apply JQuery transformation and keep a reference 
     this.percentTitle = this.chart.find(".percent"); // Keep a reference to the title 
    } 

Сделав это, на то, что ваш «обновить» метод, а не делать этого любые вызовы SetState, вместо этого, называют любой метод обновления ваш компонент может JQuery есть, вот так ...

 componentWillMount(){ 
      this.interval = setInterval(this._doRefresh.bind(this), 1500); 
     } 

     _doRefresh(percentage){ 
      // Note how setState is NOT being called here 
      percentage = percentage || Math.floor (Math.random() * 100) // simulate since we're not getting it yet 
      this.chart.data('easyPieChart').update(percentage); // call easyPieChart's update 
      this.percentTitle.text(percentage); 
     } 

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

Если, в отличие от меня, вам не повезло, что ваш компонент не имеет метода обновления, и вы не можете его сделать, возможно, пришло время полностью пересмотреть подход

7

Конечно, есть такая методика. Мы все это делаем все время.

  1. Вы создаете компонент React для переноса плагина jQuery.
  2. Внутри вашего render(), вы возвращаете пустую <div ref="placeholder" />
  3. В вашем методе componentDidMount, вы получить этот элемент его исх и инициализировать плагин JQuery там.
  4. В вашем componentWillUnmount вы его очищаете. Вызов «уничтожить» или что-то еще, чтобы избежать утечек памяти.

Все. К счастью, совершенно безопасно модифицировать DOM таким образом в React.

Если вы хотите, чтобы этот плагин реагировал на изменения реквизита, все становится немного сложнее. Вам необходимо переопределить другие методы жизненного цикла, например componentWillReceiveProps, проверить, когда реквизит действительно изменился, и вызвать соответствующие методы плагина. Я могу объяснить более подробно, если у вас появятся конкретные вопросы, общая тема слишком широка для комментария.

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