0

I am trying to animate a header on a personal site I'm working on. It's a simple fade in fade out effect as the user scrolls through the page. To get this effect what i'm doing is using an Intersection Observer to check wether or not a certain element is in view or not. When that element is in view, i'd like to change the state elementInView to reflect that or not using a boolean value. when I console.log the isIntersecting value of the element that the observer is listening to, I can see that it changes True or False based on whether the element is in view or not. so... since that is working, i'm using that value to set state of elementInView. BUT!!! the state is not changing when the element is in view, even though it should. I'm not sure what went wrong but I cannot figure it out for the life of me. here's the code:

import React from 'react';
import { useEffect, useRef, useState } from 'react';
import { Link, useRouteMatch, useNavigate } from 'react-router-dom';
import BoulderImage from "../data/nat_pic_boulders_and_tree.jpeg";
import FenceImage from "../data/nat_pic_fence.jpeg";
import GrassField from '../data/nat_pic_grass.jpeg';


export default function HomePage() {
    const navigate = useNavigate();
    const firstRef = useRef();
    const secondRef = useRef();
    const [elementInView, setElementInView] = useState();

    useEffect(() => {
        const observer = new IntersectionObserver((entries) => {
            const entry = entries[0];
            setElementInView(entry.isInteresecting);
            console.log(elementInView);
        })
        observer.observe(firstRef.current);
        observer.observe(secondRef.current);
    }, [elementInView])

        return (
            <div>
                <h1 className="main-intro-header">Welcome Home</h1>
                <div className='nav-links'>
                    <Link to="/">Home</Link>
                    <Link to="/about">About</Link>
                </div>
                <div className="stop-point-one" ref={firstRef}>stop point</div>
                <div className="stop-point-two" ref={secondRef}>stop point</div>
            </div>
)}

the list at the end of the useEffect hook. i've tried putting elementInView in there and taking it out and re-running it. no luck.

3 Answers3

0

I think that the thing you are missing is that after calling setElementInView it does not change elementInView on the current render. Only in the next render for that component elementInView will get the new state. And that way you are getting the wrong output to the console.

The way to solve it is to add another useEffect hook with elementInView as a dependency and do you logic there.

You can watch a great video of Jack Herrington regarding this:

https://www.youtube.com/watch?v=RAJD4KpX8LA

Sagie
  • 996
  • 3
  • 12
  • 25
0

Henrys comment links to a better explanation than I can give. Summed up, since setState is performed in an async nature, the value will not be what you set it to immediately after the setState line is ran. If you move your console.log line out of your observer declaration, into the body of the useEffect, it should work as you expect. I put together a little sandbox and spaced some elements apart to put them off screen to show this as well.

https://codesandbox.io/s/observer-test-m5qh88?file=/src/App.js

If you scroll the elements on and off the page with the console open, you'll see it update each time they move off and back onto the screen.

JDev
  • 73
  • 1
  • 8
0

I figured it out! I saw a simpler way to set the state of these elements as they appear on the screen. i updated this piece of code:

const [elementInView, setElementInView] = useState();

useEffect(() => {
    const observer = new IntersectionObserver((entries) => {
        const entry = entries[0];
        setElementInView(entry.isInteresecting);
        console.log(elementInView);
    })
    observer.observe(firstRef.current);
    observer.observe(secondRef.current);
}, [elementInView])

to this:

import { useInView } from 'react-intersection-observer';


const { ref: firstRef, inView: firstBreakIsVisibile} = useInView();
const { ref: secondRef, inView: secondBreakIsVisibile} = useInView();

I used npm to install the react-intersection-observer dependency.