React:方法在componentDidMount上返回错误的值

时间:2019-04-16 13:57:24

标签: javascript reactjs

我只想在视口中的组件中做某事。它几乎可以正常工作,但是我有一个无法解决的问题。

class Hello extends React.Component {

  componentDidMount() {
    console.log(this.isInViewPort()) // false for second element - last in viewport
    if (this.isInViewPort()) {
    console.log("I'm in viewport!");
    }
    window.addEventListener('scroll', this.handleScroll);
  }

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

  isInViewPort = () => {
    const element = this.refs[this.props.id];
    const rect = element.getBoundingClientRect();
    const windowHeight = window.innerHeight || document.documentElement.clientHeight;
    const result = rect.top <= windowHeight && rect.top + rect.height >= 0;
    return result;
  }

  handleScroll = () => {
    if (this.isInViewPort()) {
        console.log("I'm in viewport!");
    }
  }

  render() {
    return (
        <div className="example" ref={this.props.id} onClick={this.handleScroll} /> // onClick added for tests
    );
  }
}


class App extends React.Component {
  render() {
    return (
      <Hello id={1} />
      <Hello id={2} />
      <Hello id={3} />
    );
  }
}


.example {
  width: 400px;
  height: 400px;
  background-color: red;
}

加载页面时,视口中有两个元素。第一个-视口中的100%,第二个-视口中的15-20%。

在此示例中,我只有一个console.log,因此this.isInViewPort()仅对第一个元素为true。是错误的,因为其中两个可见。

当我触摸事件滚动时,该组件将立即显示为方法并显示控制台日志。当我不触摸滚动条,而是单击第二个组件(这是为了测试而添加)时,方法返回true,我得到console.log。

我想让每个可见元素都通过方法返回true的情况。

当我这样做时它会起作用:

componentDidMount() {
    setTimeout(() => {
      if (this.isInViewPort()) {
        console.log("I'm in viewport!");
    }, 0);
}

但是对于100%而言,这不是一个好的解决方案。当我将此代码重构为componentDidUpdate()后,它也无法正常工作。

这与反应生命周期有关吗?我没有做任何巫婆状态,这就是为什么它对我来说是无法理解的。

3 个答案:

答案 0 :(得分:1)

行得通

class Hello extends React.Component {
  componentDidMount() {
    this.isInViewPort()
    window.addEventListener('scroll', this.handleScroll);
  }

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

  isInViewPort = () => {
    const element = this.refs[this.props.id];
    const rect = element.getBoundingClientRect();
    const windowHeight = window.innerHeight || document.documentElement.clientHeight;
    const result = rect.top <= windowHeight && rect.top + rect.height >= 0;
    this.props.onLog(result ? `${this.props.id} in view port` : `${this.props.id} outside`)
    return result;
  }


  handleScroll = () => {
    this.isInViewPort()
  }

  render() {
    return (<div className="example" ref={this.props.id} onClick={this.handleScroll} />
    );
  }
}
class App extends React.Component {
  render() {
    return <div>
      <Hello id={1} onLog={console.info} />
      <Hello id={2} onLog={console.info} />
      <Hello id={3} onLog={console.info} />
     </div>;
  }
}

ReactDOM.render(<App /> ,
  document.getElementById('container')
);
.example {
  width: 400px;
  height: 400px;
  background-color: red;
}

.example:first-child {
  background-color: pink;
}

.example:last-child {
  background-color: blue;
}
.as-console-wrapper {
  height: 62px!important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="container" />

答案 1 :(得分:1)

我相信您的问题可能与您在哪里测试项目以及如何交付文件等有关。 我创建了一个小提琴来对其进行测试:

(tr,test) = dataframe.randomSplit([0.8,0.2]), seed = 23)

https://jsfiddle.net/284Ldvkc/

我只将高度从400px更改为90vh,所以我要确保始终以1%的比例显示1,以2%的比例显示2。

答案 2 :(得分:1)

在StackOverflow中,您可以找到检查“ DOM element is visible in the current viewport”的最佳方法。

我刚刚添加了一个:

isElementInViewport = el => {
    var rect = el.getBoundingClientRect();

    return (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <=
        (window.innerHeight ||
          document.documentElement.clientHeight) /*or $(window).height() */ &&
      rect.right <=
        (window.innerWidth ||
          document.documentElement.clientWidth) /*or $(window).width() */
    );
  };

并从以下位置调用它:

isInViewPort = () => {
    const element = this.refs[this.props.id];
    console.log("isInViewPort:" + this.props.id);
    return this.isElementInViewport(element);
  };

您可以在codesandbox.io

上对此进行测试