web-dev-qa-db-fra.com

Obtenez les positions compensées en haut de la division dans React

J'essaie d'implémenter une vue de liste dans React. Ce que j'essaie de faire est de stocker les informations d'en-tête de liste, d'enregistrer les composants et d'enregistrer l'événement de défilement . Chaque fois que l'utilisateur fait défiler la fenêtre, j'aimerais -calculer les données offsetTop.

Le problème maintenant, c’est que j’ai trouvé que la console n’imprimait que la valeur initiale (la valeur est fixe et jamais modifiée) Les données offsetTop ne changent jamais dans la fonction onscroll.

Quelqu'un suggère-t-il comment obtenir la dernière variable offsetTop à partir de l'objet _instances?

import React, { Component } from 'react';
import ListHeader from './lib/ListHeader';
import ListItems from './lib/ListItems';

const styles = {
  'height': '400px',
  'overflowY': 'auto',
  'outline': '1px dashed red',
  'width': '40%'
};

class HeaderPosInfo {
  constructor(headerObj, originalPosition, originalHeight) {
    this.headerObj = headerObj;
    this.originalPosition = originalPosition;
    this.originalHeight = originalHeight; 
  }
}

export default class ReactListView extends Component {
  static defaultProps = {
    events: ['scroll', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll', 'resize', 'touchmove', 'touchend'],
    _instances:[],
    _positionMap: new Set(),
    _topPos:'',
    _topWrapper:''
  }

  static propTypes = {
    data: React.PropTypes.array.isRequired,
    headerAttName: React.PropTypes.string.isRequired,
    itemsAttName: React.PropTypes.string.isRequired,
    events: React.PropTypes.array,
    _instances: React.PropTypes.array,
    _positionMap: React.PropTypes.object,
    _topPos: React.PropTypes.string,
    _topWrapper: React.PropTypes.object
  };

  state = {
    events: this.props.events,
    _instances: this.props._instances,
    _positionMap: this.props._positionMap,
    _topPos: this.props._topPos
  }

  componentDidMount() {
    this.initStickyHeaders();
  }

  componentWillUnmount() {

  }

  componentDidUpdate() {

  }

  refsToArray(ctx, prefix){
    let results = [];
    for (let i=0;;i++){
      let ref = ctx.refs[prefix + '-' + String(i)];
      if (ref) results.Push(ref);
      else return results;
    }
  }

  initHeaderPositions() {
    // Retrieve all instance of headers and store position info
    this.props._instances.forEach((k)=>{
      this.props._positionMap.add(new HeaderPosInfo(
          k, 
          k.refs.header.getDOMNode().offsetTop,
          k.refs.header.getDOMNode().offsetHeight
        ));
    });
    let it = this.props._positionMap.values();
    let first = it.next();
    this.props._topPos = first.value.originalPosition;
    this.props._topWrapper = first.value.headerObj;
  }

  initStickyHeaders () {
    this.props._instances = this.refsToArray(this, 'ListHeader');
    this.initHeaderPositions();

    // Register events listeners with the listview div
    this.props.events.forEach(type => {
      if (window.addEventListener) {
        React.findDOMNode(this.refs.listview).addEventListener(type, this.onScroll.bind(this), false);
      } else {
        React.findDOMNode(this.refs.listview).attachEvent('on' + type, this.onScroll.bind(this), false);
      }
    });
  }

  onScroll() {

    // update current header positions and apply fixed positions to the top one
    console.log(1);
    let offsetTop  = React.findDOMNode(this.props._instances[0].refs.header).offsetTop;

  }

  render() {
    const { data, headerAttName, itemsAttName } = this.props;
    let _refi = 0;
    let makeRef = () => {
      return 'ListHeader-' + (_refi++);
    };

    return (
      <div ref="listview" style={styles}>
      {
        Object.keys(data).map(k => {
        const header = data[k][headerAttName];
        const items  = data[k][itemsAttName];
          return (
            <ul key={k}>     
              <ListHeader ref={makeRef()} header={header} />
              <ListItems  items={items} />
            </ul>
          );
        })
      }
      </div>
    );
  }
}

Le code source entier est sur Github, vous pouvez le cloner et le compiler à partir d’ici:

Github

35
JavaScripter

Vous pouvez être encouragé à utiliser la méthode Element.getBoundingClientRect () pour obtenir le décalage supérieur de votre élément. Cette méthode fournit les valeurs de décalage complètes (gauche, haut, droite, bas, largeur, hauteur) de votre élément dans la fenêtre.

Lisez le message de John Resig décrivant l’utilité de cette méthode.

52
Eugene Tiurin

La réponse d'Eugene utilise la fonction correcte pour obtenir les données, mais pour la postérité, j'aimerais préciser comment l'utiliser dans React v0.14 + (selon cette réponse ): 

  import ReactDOM from 'react-dom';
  //...
  componentDidMount() {
    var rect = ReactDOM.findDOMNode(this)
      .getBoundingClientRect()
  }

Cela fonctionne parfaitement pour moi et j'utilise les données pour faire défiler vers le haut le nouveau composant qui vient d'être monté.

20
sonarforte
  import ReactDOM from 'react-dom';
  //...
  componentDidMount() {
    var n = ReactDOM.findDOMNode(this);
    console.log(n.offsetTop);
  }

Vous pouvez simplement récupérer l'offsetTop du nœud.

6
rhigdon

Une meilleure solution avec ref pour éviter findDOMNode qui soit découragée.

...
onScroll() {
    let offsetTop  = this.instance.getBoundingClientRect().top;
}
...
render() {
...
<Component ref={(el) => this.instance = el } />
...
6
Pietro

Si vous utilisez React 16.3 et versions ultérieures, vous pouvez rapidement créer une référence dans le constructeur, puis l'associer au composant que vous souhaitez utiliser, comme indiqué ci-dessous.

...
constructor(props){
   ...
   //create a ref
   this.someRefName = React.createRef();

}

onScroll(){
let offsetTop = this.someRefName.current.offsetTop;

}

render(){
...
<Component ref={this.someRefName} />

}

4
Chikwado

Je me rends bien compte que l'auteur pose une question sur un composant basé sur les classes, mais je pense qu'il est utile de mentionner que, à compter de React 16.8.0 (6 février 2019) vous pouvez tirer parti des points d'ancrage dans les fonctions Composants.

Exemple de code:

import { useRef } from 'react'

function Component() {
  const inputRef = useRef()

  return (
    <input ref={inputRef} />
    <div
      onScroll={() => {
        const { offsetTop } = inputRef.current
        ...
      }}
    >
  )
}
1
Powell_v2