reactjsreact-intersection-observer

How to change background video on scroll in React?


I am trying to change background video on scroll, using react-intersection-observer. When inView changes to true, useEffect must change state to sample2, and send new video to startscreen, where it's using as background video with position fixed. State is changing, but video is still the same.

//Startscreen

    const Startscreen = ({ sample }) => {
console.log(sample);
      return (
          <video className="videoTag" autoPlay loop muted>
            <source src={sample} type="video/mp4" />
          </video>
      );
    };

//App.js

import sample1 from "./videos/0.mov";
import sample2 from "./videos/2.mov";

function App() {
  const [state, setState] = useState(sample1);
  const { ref, inView, entry } = useInView({
    threshold: 0.5,
  });

  useEffect(() => {
    if (inView === true) {
      setState(sample2);
    } else {
      setState(sample1);
    }
    console.log(state);
  }, [inView]);

  return (
    <div className="App">
      <Startscreen sample={state} />
      <div ref={ref}>
        <AboutMe> 
    </div>
    </div>
)};

Solution

  • When you change the source of a video, the <video/> element doesn't reload, as explained in this answer : https://stackoverflow.com/a/47382850.

    One fix would be

      return (
          <video key={sample} className="videoTag" autoPlay loop muted>
            <source src={sample} type="video/mp4" />
          </video>
      );
    

    Some notes on your App implementation

    You don't need the useEffect at all since your component will re-render when inView changes. So you don't actually need a state at all because you can compute your sample from inView. Try this instead:

    function App() {
      const { ref, inView, entry } = useInView({
        threshold: 0.5,
      });
      const sample = inView ? sample2 : sample1;
      console.log(sample);
    
      return (
        <div className="App">
          <Startscreen sample={sample} />
          <div ref={ref}>
            <AboutMe> 
          </div>
        </div>
    )};
    

    Also please note that in your original code, when you are calling console.log(state);, you are actually calling your previous state value, not the state value that you have just set.

    Indeed, if your state is sample1, calling setState(sample2); then console.log(state); will print sample1. state will equal sample2 after the next re-render caused by this setState, but you are not logging on each render, only when inView changes. You can manage to also log the new state in the re-rendering by adding state to useEffect dependency array:

    useEffect(() => {
        if (inView === true) {
          setState(sample2);
        } else {
          setState(sample1);
        }
        console.log(state);
      }, [inView, state]);