相当于React,onScroll中的document.querySelectorAll()

时间:2018-03-18 03:58:49

标签: javascript reactjs

React中的onScroll相当于什么?我知道我应该使用Refs,但是我如何观察多个Refs <ul> <li data-position="below-viewport"></li> <li data-position="below-viewport"></li> <li data-position="below-viewport"></li> <li data-position="below-viewport"></li> </ul>

我通常使用类似下面的函数来检查页面中多个元素的视口位置,并在每个元素进入视口时触发不同的css动画:

HTML

getPosition: function (element) {
  const rect = element.getBoundingClientRect();

  if ((rect.top > -1) && (rect.top < (window.innerHeight * 0.75))) {
    element.setAttribute('data-js-position','in-viewport');
  } else if ((rect.top > 0) && (rect.top < window.innerHeight)) {
    element.setAttribute('data-js-position','entering-viewport');
  } else if (rect.top > window.innerHeight) {
    element.setAttribute('data-js-position','below-viewport');
  } else if (rect.bottom < 0) {
    element.setAttribute('data-js-position','above-viewport');
  }
}

window.addEventListener('scroll', function() {
  [].forEach.call(document.querySelectorAll('[data-js-position]'), el => {
    positionChecker.getPosition(el);
  })
});

的Javascript

App.js

我如何在React中实现类似的东西?你能给我一个在React中观察多个div的函数的例子吗?

更好:我如何在{{1}}中抽象这个函数,以便我也可以在子组件中使用它?

3 个答案:

答案 0 :(得分:3)

使每个li html元素成为自己的组件,并在其自己的状态下保存ref个引用。

class LiElement extends React.Component {
  componentDidMount() {
    this.ref.getPosition()
  }

  render() {
    return (
      <li ref={(ctx) => this.ref = ctx}>
      </li>
    )
  }
}

答案 1 :(得分:0)

通过refs迭代可能会让你得到类似于你想要实现的东西。

在这个例子中,我将每个节点存储在本地Map中,这样你就可以遍历它们并得到它们的getBoundingClientRect。

注意,有几种方法可以做到这一点,你不必创建一个Map,你可以只获得每个元素的“ref”,但是你必须给它分配一个不同的“ref”值每个“li”。

此外,最好限制handleScroll调用。

class App extends React.Component {
  constructor(props) {
    super(props);
    this._nodes = new Map();
  }

  componentDidMount() {
    window.addEventListener("scroll", this.handleScroll);
  }

  handleScroll = e => {
    Array.from(this._nodes.values())
      // make sure it exists
      .filter(node => node != null)
      .forEach(node => {
        this.getPosition(node);
      });
  };

  getPosition = node => {
    const rect = node.getBoundingClientRect();
    // do something cool here
    console.log("rect:", rect);
  };

  componentWillUnmount() {
    window.removeEventListener("scroll", this.handleScroll);
  }

  render() {
    return (
      <div>
        <ul>
          <li key="1" ref={c => this._nodes.set(1, c)}>
            Your thing 1
          </li>
          <li key="2" ref={c => this._nodes.set(2, c)}>
            Your thing 2
          </li>
          <li key="3" ref={c => this._nodes.set(3, c)}>
            Your thing 3
          </li>
        </ul>
      </div>
    );
  }
}

答案 2 :(得分:0)

添加其他可能的解决方案:创建一个新的反应组件VisibilitySensor,以便只使用一个ref和一个函数

import React, { Component } from 'react';

class VisibilitySensor extends Component {

  componentDidMount() {
    window.addEventListener('scroll', this.getElementPosition = this.getElementPosition.bind(this));
    this.getElementPosition();
  }

  componentWillUnmount() {
    window.removeEventListener('scroll', this.getElementPosition);
  }

  getElementPosition() {
    const element = this.visibilitySensor;
    var rect = element.getBoundingClientRect();

    if ((rect.top > 0) && (rect.top < (window.innerHeight * 0.75))) {
      element.setAttribute("data-position","in-viewport");
    } else if (rect.top > window.innerHeight) {
      element.setAttribute("data-position","below-viewport");
    } else if (rect.bottom < 0) {
      element.setAttribute("data-position","above-viewport");
    }
  }

  render() {
    return (
      <div data-position="below-viewport" ref={(element) => { this.visibilitySensor = element; }}>
        {this.props.children}
      </div>
    );
  }
}

export default VisibilitySensor;

然后包装每个li(或div)我需要注意上面的组件。我个人最终不喜欢上面的解决方案,因为新添加的div会破坏我的CSS样式,特别是与父级宽度相关的子宽度等。

 <ul>
  <VisibilitySensor>
    <li></li>
  </VisibilitySensor>
  <VisibilitySensor>
    <li></li>
  </VisibilitySensor>
  <VisibilitySensor>
    <li></li>
  </VisibilitySensor>
  <VisibilitySensor>
    <li></li>
  </VisibilitySensor>
</ul>