反応コンポーネント内のDOM要素をターゲットに設定するにはどうすればよいですか?または、DOM要素をすべて一緒にターゲットにしないようにする必要がありますか?


8

親コンテナー上でマウスオーバーするとアクティブになるスクリプトを作成し、その子要素をマウスから離す必要があります。私は現在それを機能させていますが、コードのいくつかの部分は、REACTコードがどのように見えるべきかと矛盾しているようです。特に2つの部分。

  1. レンダー関数でカウンターを使用して、各スパンが正しいカスタムプロパティを取得できるようにしています。state.customPropertiesこれは、親要素のマウスオーバー時にカスタムプロパティを更新する配列です。

    render() {
        let counter = 0;
        const returnCustomProp = function(that) {
            counter++;
            let x = 0;
            if (that.state.customProperties[counter - 1]) {
                x = that.state.customProperties[counter - 1].x;
            }
            let y = 0;
            if (that.state.customProperties[counter - 1]) {
                y = that.state.customProperties[counter - 1].y;
            }
            return "customProperty(" + x + " " + y + ")";
        }
        return (
            <div onMouseMove={this._testFunction} id="ParentContainer">
                    <section custom={returnCustomProp(this)}>
                        <b>Custom content for part 1</b>
                        <i>Could be way different from all the other elements</i>
                    </section>
                    <section custom={returnCustomProp(this)}>
                        2
                    </section>
                    <section custom={returnCustomProp(this)}>
                        <div>
                            All content can differ internally so I'm unable to create a generic element and loop trough that
                        </div>
                    </section>
                    <section custom={returnCustomProp(this)}>
                        4
                    </section>
                    <section custom={returnCustomProp(this)}>
                        5
                    </section>
                    <section custom={returnCustomProp(this)}>
                        <div>
                            <b>
                                This is just test data, the actualy data has no divs nested inside secions
                            </b>
                            <h1>
                                6
                            </h1>
                        </div>
                    </section>
                    <section custom={returnCustomProp(this)}>
                        7
                    </section>
                    <section custom={returnCustomProp(this)}>
                        8
                    </section>
                </div>
        );
    }
  2. 私が使用document.getElementByIdquerySelectorAllているmousemove関数では、すべてのセクション要素を取得し、マウスからのマウス座標をセクション要素の座標と比較しています。

    var mouseX = e.pageX;
    var mouseY = e.pageY;
    var spans = document.getElementById('ParentContainer').querySelectorAll('section');
    var theRangeSquared = 10 * 10;
    var maxOffset = 5;
    var newCustomProperties = [];
    for (var i = 0; i < spans.length; i++) {
        var position = spans[i].getBoundingClientRect();
        var widthMod = position.width / 2;
        var heightMod = position.height / 2;
        var coordX = position.x + widthMod;
        var coordY = position.y + heightMod + window.scrollY;
        // Distance from mouse
        var dx = coordX - mouseX;
        var dy = coordY - mouseY;
        var distanceSquared = (dx * dx + dy * dy);
        var tx = 0,
            ty = 0;
        if (distanceSquared < theRangeSquared && distanceSquared !== 0) {
            // Calculate shift scale (inverse of distance)
            var shift = maxOffset * (theRangeSquared - distanceSquared) / theRangeSquared;
            var distance = Math.sqrt(distanceSquared);
            tx = shift * dx / distance;
            ty = shift * dy / distance;
        }
        newCustomProperties.push({
            x: tx,
            y: ty
        });
    }

私はこれについてすべて間違っていると感じています。returnCustomProp上記の要素のプロパティを返す汎用関数を保持しながら、カウンターをどのように回避できるかわかりません(ライブコードでは、これらの要素が約200あるため、手動で配列項目番号を設定することは効率的ではありません)。 。

2番目の部分は、実際のコンポーネント内にあるIDで要素をターゲットにするのはハッキーです。DOMをトラバースせずにこれをターゲットにできるはずです。セクション要素を参照することは解決策になる可能性がありますが、参照は最小限に留めるべきであり、実際のコードは数百のこれらのセクションで構成されています。

JSFIDDLE

コードは、custom="customProperty(0 0)"プロパティを更新するよりも多くのatmを実行しません。これは、マウスオーバー時に要素インスペクターを介して行われることがわかります。

<section>render関数内の要素を数えることなく、また使用することなく、この機能を動作させることができdocument.querySelectorAllますか?

回答:


7

反応コンポーネント内のDOM要素をターゲットにするにはどうすればよいですか?

Reactの公式ドキュメントによれば、refを使用してdom要素にアクセスできます。React.createRef()呼び出しを使用して参照を作成する必要があります。

DOM要素を完全にターゲットにすることを避けるべきですか?

特定のdom要素を取得するためにdomをトラバースすることは、パフォーマンスの問題を引き起こすため、Reactではお勧めできません。ただし、reactでは、を使用して同じことを行う別の方法を使用できますcreateRef()

render関数内の要素を数えたり、document.querySelectorAllを使用したりすることなく、この機能を機能させることができますか?

はい、これを実装するには、次の手順を検討してください。

コンストラクター内で、次のようにparentContainerの参照を作成します。

  this.ParentContainer=React.createRef();

次に、レンダーでparentContainer refを使用します。

    <div onMouseMove={this._testFunction} id="ParentContainer" 
      ref={this.ParentContainer} >

テストコンポーネント内で、this.parentContainerを次のように使用します。

//replace this 
//var spans = document.getElementById('ParentContainer').querySelectorAll('section');
//with this
  var spans = this.parentContainer.current.childNodes;

確認できます ここでます

編集

render関数内でletカウンターを使用する必要がある場合の回避策についての考え

次のreturnCustomPropように外部レンダーを定義できます(ここでは、this参照の代わりに各セクションのインデックスを渡す必要があります)。

    returnCustomProp = (index)=> {
      let x = 0;
      if(this.state.customProperties[index]) {
          x = this.state.customProperties[index].x;
      }
      let y = 0;
      if(this.state.customProperties[index]) {
          y = this.state.customProperties[index].y;
      }
      return "customProperty("+x+" "+y+")";
    }

次のようなセクションで使用します。

   <section custom={returnCustomProp(0)}>
            <b>Custom content for part 1</b>
            <i>Could be way different from all the other elements</i>
        </section>
        <section custom={returnCustomProp(1)}>
            2
        </section>
        <section custom={returnCustomProp(2)}>
            <div>
                All content can differ internally so I'm unable to create a generic element and loop trough that
            </div>
        </section>
        <section custom={returnCustomProp(3)}>
            4
        </section>
        <section custom={returnCustomProp(4)}>
            5
        </section>
        <section custom={returnCustomProp(5)}>
            <div>
                <b>
                    This is just test data, the actual data has no divs nested inside sections
                </b>
                <h1>
                    6
                </h1>
            </div>
        </section>
        <section custom={returnCustomProp(6)}>
            7
        </section>
        <section custom={returnCustomProp(7)}>
            8
        </section>

ああ、私はそれぞれの個々の要素に参照を置かなければならないだろうと考えました。そのような子要素を介してクエリを実行できることを知りませんでした。これは、DOMのターゲット設定に最適です(this.parentContainerところでエラーが発生した場合は、this.ParentContainer大文字にする必要があります)。let counterレンダー関数の内部を使用する必要があることを回避するにはどうすればよいですか?
timo

1.大文字にするとthis.ParentContainerになるはずです:はい 私は与えられたフィドルでそれを削除しようとしました
Jatin Parmar

多分私はあなたのフィドルの間違ったバージョンを見ていますが、私は49-61行目について話しています。できれば、これらをレンダー機能の外に移動します。おそらく、これは元の質問の範囲外です。
timo

カウンターパートのソリューションは、がたくさんある場合には非効率的ですsections。関数をジェネリックにしたいと思います。質問から:(in the live code I've got about 200 of these elements so setting the array item number for them manually is not efficient).おそらく、render関数内でカウントすることが、おそらく最も効率的な方法です。
timo

0

仮想DOMの反応プロセスのため、DOMを直接操作しないでください。React docによると、Ref転送を使用する必要があります。詳しくはこちらをご覧ください


これでフィドルを提供できますか?また、参照の使用に関する質問で、私のコメントについての情報を提供してもらえますか?: "" ...しかし、参照は最小限にとどめる必要があると思います。前述のように、実際のコードはこれらのセクションの何百もの部分で構成されています... "
timo

0

あなたが他の解決策を見つけられないが、ドキュメントが言っている方法ならばfindDOMNode()ReactDOMのメソッドを使うことができます:

findDOMNode基になるDOMノードにアクセスするために使用されるエスケープハッチです。ほとんどの場合、このエスケープハッチはコンポーネントの抽象化を貫通するため、使用しないことをお勧めします。

例として、使用しているライブラリのドラッグ&ドロップを実装するためにコンテナーDOM divへの参照が必要なアプリの使用例を共有したいと思います。

例:

import React, { forwardRef } from 'react'
import _ from 'lodash'
import { List } from 'office-ui-fabric-react'
import { Draggable } from 'react-beautiful-dnd'
import ReactDOM from 'react-dom'

export const BasicItemList = forwardRef((
  {
    items,
    onClickItem,
    innerRef,
    ...rest
  }, ref) => {
  const itemToCell = (i, idx) => {
    return (
      <Draggable draggableId={id} index={idx} key={idx}>
        {
          (provided, snapshot) => {
            return (
              <MyCell item={i}
                      onClick={onClickItem}
                      innerRef={provided.innerRef}
                      isDragging={snapshot.isDragging}
                      {...provided.dragHandleProps}
                      {...provided.draggableProps}
              />
            )
          }
        }
      </Draggable>
    )
  }
  const refGetter = (comp) => {
    const div = ReactDOM.findDOMNode(comp)
    if (_.isFunction(ref)) {
      ref(comp)
    } else if (ref) {
      ref.current = comp
    }
    if (_.isFunction(innerRef)) {
      innerRef(div)
    }
  }

  return (
    <List items={items}
          onRenderCell={itemToCell}
          componentRef={refGetter}
          {...rest}
    />
  )
})
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.