0

I don't know why the handleScroll only executes once, not when i keep scrolling up and down. And I need to somehow get the element's height but im not sure how other than documentElement. The function needs to turn a true/false so that I can do a setState and add/change a class in my heroGallery div for a css animation.

 import React, { Component } from "react";

 class Intro extends Component {
constructor(props) {
    super(props);
    this.heroRef = React.createRef();
    this.state = {};
}

componentDidMount = () => {
    this.heroRef.current.getBoundingClientRect();
    let hero2 = this.heroRef;
    console.log(this.heroRef.current);
    window.addEventListener("scroll", this.handleScroll(hero2));
};

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

handleScroll = elm => {
    var rect = elm.current.getBoundingClientRect();
    //var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);

    //return !(rect.bottom < 0 || rect.top - viewHeight >= 0);
};

render() {
    return (
        <div className="introCont">
            <div className="introFirstSection" />
            <div className="heroSection">
                <div className="heroGallery" ref={this.heroRef}>
                    <div className="slide-down">item 1</div>
                    <div>item 2</div>
                    <div>item 3</div>
                    <div>item 4</div>
                    <div>item 5</div>
                    <div>item 6</div>
                    <div>item 7</div>
                    <div>item 8</div>
                </div>
            </div>
        </div>
    );
  }
 }

 export default Intro;

EDIT: Taking out my intro component from Switch lets it work.

import React, { lazy, Suspense, Component } from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import { connect } from "react-redux";
import * as actions from "./actions";

//import asyncComponent from "./components/AsyncComponent";
//const Header = asyncComponent(() => import("./Header"));
import Intro from "./components/Intro";
const Header = lazy(() => import("./components/Header"));
const Footer = lazy(() => import("./components/Footer"));
const Landing = lazy(() => import("./components/Landing"));
const Profile = lazy(() => import("./components/Profile"));
const BookList = lazy(() => import("./components/books/BookList"));
const BookNote = lazy(() => import("./components/books/BookNote"));
const StatsChart = lazy(() => import("./components/StatsChart"));

class App extends Component {
  componentDidMount() {
    this.props.fetchUser();
  }

  render() {
    return (
      <BrowserRouter>
        <div className="rootdk">
          <Suspense fallback={<div />}>
            <Header />
          </Suspense>
          <div className="container">
            <Suspense fallback={<div className="loader" />}>
              <Switch>
                <Route exact path="/intro" component={() => <Intro />} />

                <Route
                  exact
                  path="/mybooks"
                  component={() =>
                    this.props.thisuser ? <BookList /> : <Landing />
                  }
                />
                <Route
                  exact
                  path="/booknotes"
                  component={() =>
                    this.props.thisuser ? <BookNote /> : <Landing />
                  }
                />
                <Route
                  exact
                  path="/statschart"
                  component={() =>
                    this.props.thisuser ? <StatsChart /> : <Landing />
                  }
                />
                <Route
                  exact
                  path="/profile"
                  component={() =>
                    this.props.thisuser ? <Profile /> : <Landing />
                  }
                />
                <Route exact path="/" component={Landing} />
              </Switch>
            </Suspense>

            <Suspense fallback={<div />}>
              <Footer />
            </Suspense>
          </div>
        </div>
      </BrowserRouter>
    );
  }
}

function mapStateToProps(props) {
  return { thisuser: props.auth };
}

export default connect(
  mapStateToProps,
  actions
)(App);

2 Answers 2

3

It is because you are invoking the method in your addEventListener.

Demo: https://codesandbox.io/s/nv16oq6j?fontsize=14

Change your componentDidMount function to this.

componentDidMount = () => {
    this.heroRef.current.getBoundingClientRect();
    let hero2 = this.heroRef;
    console.log(this.heroRef.current);
    window.addEventListener("scroll", () => this.handleScroll(hero2));
};

All I did is make the second argument of addEventListener as a function.

Sign up to request clarification or add additional context in comments.

3 Comments

hasn't helped. it seems like the event handler isnt even firing. i dont see a consolelog from the handleScroll
It should work!! here the links for demo codesandbox.io/s/nv16oq6j?fontsize=14. You need to do error handling
thanks for the example. it seems as soon as I take out my Intro component from under my Suspense wrapper it works.
0

Another answer would be to use the HTML5 API, which makes it much much easier: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

Also, the container obviously has to have a fixed height, and have the overflow property set to auto or scroll.

Example:

import React, { Component } from "react";
import ReactDOM from "react-dom";

class Intro extends Component {
  constructor(props) {
    super(props);
    this.heroRef = React.createRef();
    this.state = {
      heroanim: "no"
    };
  }

  componentDidMount = () => {
    setTimeout(() => {
      this.startup();
    }, 500);
    this.startup();
  };

  componentWillUnmount = () => {};

  startup = () => {
    //the class we want to observe for visibility changes, scrolling for animation.
    let adBox = document.querySelector(".heroSection");

    //checks if page was tabbed out or hidden. separate from intersectionobserver
    document.addEventListener(
      "visibilitychange",
      this.handleVisibilityChange,
      false
    );
    //intersectionobserveroptions. root is highest element you want to compare to. threshhold is % visibility of element you're comparing to root. 'callback activated when 50% visibility.'
    let observerOptions = {
      root: null,
      rootMargin: "0px",
      threshold: [0.7]
    };

    //create new intersection observer
    let adObserver = new IntersectionObserver(
      this.intersectionCallback,
      observerOptions
    );

    //call it.
    adObserver.observe(adBox);
  };

  handleVisibilityChange = () => {
    if (document.hidden) {
      //changeclass to no animate through setstate
    } else {
      //changeclass to animate
    }
  };

  intersectionCallback = entries => {
    entries.forEach(entry => {
      let adBox = entry.target;

      if (entry.isIntersecting) {
        this.setState({ heroanim: "yes" });
      } else {
        console.log("not");

        this.setState({ heroanim: "no" });
      }
    });
  };

  render() {
    return (
      <div className="introCont">
        <div className="introFirstSection" />
        <div
          className={
            this.state.heroanim === "yes"
              ? "heroSection heroAnim"
              : "heroSection"
          }
          ref={this.heroRef}
        >
          <div className="heroGallery">
            <div className="slide-down">item 1</div>
            <div>item 2</div>
            <div>item 3</div>
            <div>item 4</div>
            <div>item 5</div>
            <div>item 6</div>
            <div>item 7</div>
            <div>item 8</div>
          </div>
        </div>
      </div>
    );
  }
}

export default Intro;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.