Como faço para segmentar elementos DOM dentro de um componente de reação ou devo evitar segmentar todos os elementos DOM?

8

Eu criei um script que é ativado na passagem do mouse em um contêiner pai e deve mover seus elementos filhos para longe do mouse. No momento, estou trabalhando, mas há algumas partes do código que parecem contradizer a aparência do código REACT. Especialmente duas partes.

  1. Estou usando um contador na função de renderização para que cada extensão obtenha sua propriedade personalizada correta, da state.customPropertiesqual é uma matriz que atualiza as propriedades personalizadas na passagem do mouse do elemento pai.

    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. Na função mousemove, estou usando document.getElementByIde querySelectorAllpara obter todos os elementos da seção e comparar as coordenadas do mouse e as coordenadas dos elementos da seção.

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

Tenho a sensação de que estou fazendo tudo errado. Não tenho certeza de como evitar o contador, mantendo uma returnCustomPropfunção genérica para retornar as propriedades do referido elemento (no código ativo, tenho cerca de 200 desses elementos, portanto, definir o número do item da matriz manualmente não é eficiente) .

A segunda parte parece hacky para direcionar um elemento por ID que está dentro do componente real. Eu sinto que deveria ser capaz de direcionar isso sem atravessar o DOM. Fazer referência aos elementos da seção pode ser uma solução, mas acredito que as referências devem ser reduzidas ao mínimo e, como afirmado, o código real consiste em centenas dessas seções.

JSFIDDLE

O código não faz muito mais atm do que atualizar a custom="customProperty(0 0)"propriedade. Você pode ver isso acontecer através do inspetor de elementos ao passar o mouse.

Posso fazer essa funcionalidade funcionar sem ter que contar os <section>elementos dentro da função de renderização e sem precisar usar document.querySelectorAll?

timo
fonte

Respostas:

7

Como direcionar elementos DOM dentro de um componente de reação?

De acordo com os documentos oficiais do React, você pode acessar os elementos dom usando refs. Você precisa criar uma referência usando a React.createRef()chamada.

Devo evitar a segmentação total dos elementos DOM?

Atravessar o dom para buscar um elemento dom específico não é uma boa prática com o React, pois causa um problema de desempenho. No entanto, reagir permite uma maneira alternativa de fazer o mesmo usando createRef().

Posso fazer essa funcionalidade funcionar sem precisar contar os elementos dentro da função de renderização e sem precisar usar document.querySelectorAll?

Sim, considere as seguintes etapas para implementar isso:

Dentro do construtor, crie uma ref para parentContainer assim:

  this.ParentContainer=React.createRef();

Em seguida, use parentContainer ref em render:

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

Dentro do componente de teste, use this.parentContainer como:

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

Você pode conferir aqui

EDITAR

Quaisquer pensamentos sobre como eu posso contornar ter que usar o contador let dentro da função de renderização

Você pode definir returnCustomProprenderização externa como esta: (Aqui você precisará passar o índice de cada seção em vez de uma thisreferência)

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

E use-o com uma seção como esta:

   <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>
Jatin Parmar
fonte
Ah, imaginei que teria que colocar uma ref em cada elemento individual. Não sabia que eu poderia consultar os elementos filhos assim. Isso é perfeito para a parte de segmentação do DOM. (Ocorreu um erro em this.parentContainerbtw, deve ser this.ParentContainermaiúsculo). Alguma idéia de como eu posso resolver o problema de usar a let counterfunção de renderização dentro?
timo
1. deve ser isso. Eu tentei removê-lo em determinado fiddle
Jatin Parmar
Talvez esteja vendo a versão errada do seu violino, mas estou falando das linhas 49-61. De preferência, eu os moveria para fora da função de renderização. Talvez isso esteja fora do escopo da pergunta original.
timo
A solução para a parte contrária é ineficiente para quando há muito sections. Eu gostaria de manter a função genérica. Da pergunta: (in the live code I've got about 200 of these elements so setting the array item number for them manually is not efficient).Talvez contar dentro da função render seja a maneira mais eficiente, afinal.
timo
0

Você não deve manipular o DOM diretamente, porque reage ao processo no dom virtual. De acordo com o documento React, você deve usar o encaminhamento de referência. Para mais detalhes , leia isto .

Masih Jahangiri
fonte
Você poderia fornecer um violino com isso? Além disso, você poderia fornecer informações sobre o meu comentário declarado na pergunta sobre o uso de referências? : "" ... mas acredito que as referências devem ser reduzidas ao mínimo e, como indicado, o código real consiste em centenas dessas seções ... "
timo
0

Você pode usar o método findDOMNode()de ReactDOM se você não encontrar outra solução, mas como a documentação disse:

findDOMNodeé uma escotilha de escape usada para acessar o nó DOM subjacente. Na maioria dos casos, o uso dessa escotilha de escape é desencorajado porque perfura a abstração do componente.

Como exemplo, quero compartilhar um caso de uso do meu aplicativo em que preciso da referência à div DOM do contêiner para implementar o arrastar e soltar para as bibliotecas que eu estava usando.

Exemplo:

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}
    />
  )
})
93sauu
fonte