web-dev-qa-db-ja.com

React Components?でDOMを変換するJQueryプラグインの使用

一部のJQueryプラグインは、DOMノードに動作を追加するだけでなく、変更します。たとえば、 ブートストラップスイッチ ターン

_<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コンポーネントに組み込むための推奨テクニックはありますか?または、Reactの仮定を根本的に破って動作しませんか?

47
tnunamak

いいえ、reactは、reactの外にあるコンポーネントのdom構造を変更するものにひどく反応します(笑)。これはやりたくないことです。推奨される解決策は、jqueryまたは同様のプラグインで何をしようとしているかに関係なく、機能を複製することです。

そうは言っても、それなしではできない特定のインスタンスに対してこれを行う合理的な方法がありますが、本質的には、react内にいくつかの非反応domをラップすることを意味します。

例:

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>
        )
    }
});

もちろん、ネストされたdivでコンポーネントをレンダリングしています。ネストされたdivが初めてdomにマウントされると、ネストされたdivはjqueryによってチェックボックスが追加され、jQueryプラグインも実行されます。

この特定の例のコンポーネントにはほとんど意味がありませんが、これをより複雑なコンポーネントに統合しながら、再レンダリングして状態の変化などに反応できるようにする方法を見ることができます。反応に関する限り、問題のチェックボックス内に存在するものは存在しません。

上の例では、ネストされたdivを追加/削除する反応ロジックを使用する場合、チェックボックスを再挿入して再初期化するために、挿入されるdivの周りに同じロジックが必要です。 jqueryプラグイン。ただし、reactは必要な場合にのみdomを変更するため、コンテナdivをdomに削除/再レンダリングする方法で変更を加えない限り、この挿入されたdomコンテンツは削除されません。つまり、そのコンテナdivなどのreact内のすべてのイベントに引き続きアクセスできます。

また、componentDidMount関数を使用して、チェックボックス自体の特定の相互作用にイベントまたはコールバックをバインドするように反応させることもできます。 componentWillUnmountで、または特定のケースのコンポーネントライフサイクルでそうすることが理にかなっている場所で、それらを正しくバインド解除してください。

58
Mike Driver

この素晴らしいryanflorenceのチュートリアルでは、これを行う方法についてのアイデアを得ることができます。

DOMライブラリのラッピング

方法論

  1. DOMライブラリは通常DOMを操作します
  2. Reactは再レンダリングを試み、前回とは異なるDOMを見つけてフリークします
  3. ReactからDOM操作を隠します。レンダリングツリーを壊し、libが操作するDOMの周りに再接続します。
  4. コンポーネントの消費者はReact-landにとどまることができます。
21
mik01aj

確かに、そのようなテクニックがあります。私たちは常にこれらのことをしています。

  1. Reactコンポーネントを作成してjQueryプラグインをラップします。
  2. render()の内部では、空の<div ref="placeholder" />
  3. ComponentDidMountメソッドで、refでこの要素を取得し、そこでjQueryプラグインを初期化します。
  4. componentWillUnmountで、クリーンアップします。 「destroy」、またはメモリリークを回避するために必要な他のものを呼び出します。

それでおしまい。幸いなことに、Reactでこの方法でDOMを変更することは完全に安全です。

このプラグインが小道具の変更に反応するようにしたい場合は、少しトリッキーになります。 componentWillReceivePropsなどの他のライフサイクルメソッドをオーバーライドし、小道具が実際に変更されるたびにチェックし、対応するプラグインメソッドを呼び出す必要があります。特定の質問がある場合、全体的なトピックはコメントには広すぎます。

8
gaperton

これは哲学的な質問です

ReactはDOM操作を最適化するために作成されており、setStateを介してコンポーネントの状態が変化した場合にバックグラウンドで多くの配線を行います。

そうすると、前述の配線がその仮想DOMを横断して、更新する必要があるノードを見つけます。

Reactを使用する必要がある場合、コーディングの一貫性のレベルを維持しようとするかどうかにかかわらず、最善の方法は、そのようにcomponentDidMount内でJQuery DOM操作を適用することです...

    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
    }

そうすると、「refresh」メソッドが何であれ、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);
        }

この時点で、なぜReactを使用するのか、という場合、この場合、このコンポーネントは他のReactコンポーネントとアプリケーション全体で一貫性を維持するために使用されていました...あなたは同様のジレンマを持っている可能性があります

私とは異なり、コンポーネントに更新メソッドがないほど運が悪く、作成できない場合は、アプローチを完全に再考する時が来るかもしれません

2
David Glass