Toggle Class based on scroll React JS

后端 未结 7 1795
醉话见心
醉话见心 2021-02-01 06:38

I\'m using bootstrap 4 nav bar and would like to change the background color after ig 400px down scroll down. I was looking at the react docs and found a onScroll but couldn\'t

相关标签:
7条回答
  • 2021-02-01 07:19

    This is yet another take / my take on hooks approach for on scroll displaying and hiding of a random page element.

    I have been very much inspired from: Dan Abramov's post here.

    You can check a full working example, in this CodeSandbox demo.

    The following is the code for the useScroll custom hook:

    import React, { useState, useEffect } from "react";
    
    export const useScroll = callback => {
      const [scrollDirection, setScrollDirection] = useState(true);
    
      const handleScroll = () => {
        const direction = (() => {
          // if scroll is at top or at bottom return null,
          // so that it would be possible to catch and enforce a special behaviour in such a case.
          if (
            window.pageYOffset === 0 ||
            window.innerHeight + Math.ceil(window.pageYOffset) >=
              document.body.offsetHeight
          )
            return null;
          // otherwise return the direction of the scroll
          return scrollDirection < window.pageYOffset ? "down" : "up";
        })();
    
        callback(direction);
        setScrollDirection(window.pageYOffset);
      };
    
      // adding and cleanning up de event listener
      useEffect(() => {
        window.addEventListener("scroll", handleScroll);
        return () => window.removeEventListener("scroll", handleScroll);
      });
    };
    

    And this hook will be consumed like this:

      useScroll(direction => {
        setScrollDirection(direction);
      });
    

    A full component using this custom hook:

    import React, { useState } from "react";
    import ReactDOM from "react-dom";
    import CustomElement, { useScroll } from "./element";
    import Scrollable from "./scrollable";
    
    function Page() {
      const [scrollDirection, setScrollDirection] = useState(null);
    
      useScroll(direction => {
        setScrollDirection(direction);
      });
    
      return (
        <div>
          {/* a custom element that implements some scroll direction behaviour */}
          {/* "./element" exports useScroll hook and <CustomElement> */}
          <CustomElement scrollDirection={scrollDirection} />
          {/* just a lorem ipsum long text */}
          <Scrollable />
        </div>
      );
    }
    
    const rootElement = document.getElementById("root");
    
    ReactDOM.render(<Page />, rootElement);
    

    And lastly the code for CustomElement:

    import React, { useState, useEffect } from "react";
    
    export default props => {
      const [elementVisible, setElementVisible] = useState(true);
      const { scrollDirection } = props;
    
      // when scroll direction changes element visibility adapts, but can do anything we want it to do
      // U can use ScrollDirection and implement some page shake effect while scrolling
      useEffect(() => {
        setElementVisible(
          scrollDirection === "down"
            ? false
            : scrollDirection === "up"
            ? true
            : true
        );
      }, [scrollDirection]);
    
      return (
        <div
          style={{
            background: "#ff0",
            padding: "20px",
            position: "fixed",
            width: "100%",
            display: `${elementVisible ? "inherit" : "none"}`
          }}
        >
          element
        </div>
      );
    };
    
    0 讨论(0)
  • 2021-02-01 07:22

    One way to add a scroll listener is to use the componentDidMount() lifecycle method. Following example should give you an idea:

    import React from 'react';
    import { render } from 'react-dom';
    
    class App extends React.Component {
      state = {
        isTop: true,
      };
    
      componentDidMount() {
        document.addEventListener('scroll', () => {
          const isTop = window.scrollY < 100;
          if (isTop !== this.state.isTop) {
              this.setState({ isTop })
          }
        });
      }
      render() {
        return (
          <div style={{ height: '200vh' }}>
            <h2 style={{ position: 'fixed', top: 0 }}>Scroll {this.state.isTop ? 'down' : 'up'}!</h2>
          </div>
        );
      }
    } 
    
    render(<App />, document.getElementById('root'));
    

    This changes the Text from "Scroll down" to "Scroll up" when your scrollY position is at 100 and above.

    Edit: Should avoid the overkill of updating the state on each scroll. Only update it when the boolean value changes.

    0 讨论(0)
  • 2021-02-01 07:25

    These are two hooks - one for direction (up/down/none) and one for the actual position

    Use like this:

    useScrollPosition(position => {
        console.log(position)
      })
    
    useScrollDirection(direction => {
        console.log(direction)
      })
    

    Here are the hooks:

    import { useState, useEffect } from "react"
    
    export const SCROLL_DIRECTION_DOWN = "SCROLL_DIRECTION_DOWN"
    export const SCROLL_DIRECTION_UP = "SCROLL_DIRECTION_UP"
    export const SCROLL_DIRECTION_NONE = "SCROLL_DIRECTION_NONE"
    
    export const useScrollDirection = callback => {
      const [lastYPosition, setLastYPosition] = useState(window.pageYOffset)
      const [timer, setTimer] = useState(null)
    
      const handleScroll = () => {
        if (timer !== null) {
          clearTimeout(timer)
        }
        setTimer(
          setTimeout(function () {
            callback(SCROLL_DIRECTION_NONE)
          }, 150)
        )
        if (window.pageYOffset === lastYPosition) return SCROLL_DIRECTION_NONE
    
        const direction = (() => {
          return lastYPosition < window.pageYOffset
            ? SCROLL_DIRECTION_DOWN
            : SCROLL_DIRECTION_UP
        })()
    
        callback(direction)
        setLastYPosition(window.pageYOffset)
      }
    
      useEffect(() => {
        window.addEventListener("scroll", handleScroll)
        return () => window.removeEventListener("scroll", handleScroll)
      })
    }
    
    export const useScrollPosition = callback => {
      const handleScroll = () => {
        callback(window.pageYOffset)
      }
    
      useEffect(() => {
        window.addEventListener("scroll", handleScroll)
        return () => window.removeEventListener("scroll", handleScroll)
      })
    }
    
    0 讨论(0)
  • 2021-02-01 07:27

    For those of you who are reading this question in 2020, I've taken @glennreyes answer and rewritten it using React Hooks:

      const [scroll, setScroll] = useState(0)
    
      useEffect(() => {
        document.addEventListener("scroll", () => {
          const scrollCheck = window.scrollY < 100
          if (scrollCheck !== scroll) {
            setScroll(scrollCheck)
          }
        })
      })
    

    Bear in mind that, useState has an array of two elements, firstly the state object and secondly the function that updates it.

    Along the lines, useEffect helps us replace componentDidmount, the function written currently does not do any clean ups as it's not necessary in this case.

    If you find it essential to clean up, you can just return a function inside the useEffect.

    You can read comprehensively here.

    UPDATE:

    If you guys felt like making it modular and even do the clean up, you can do something like this:

    1. Create a custom hook as below;

      import { useState, useEffect } from "react"
      
      export const useScrollHandler = () => {
      // setting initial value to true
      const [scroll, setScroll] = useState(1)
      
      // running on mount
      useEffect(() => {
        const onScroll = () => {
          const scrollCheck = window.scrollY < 10
          if (scrollCheck !== scroll) {
            setScroll(scrollCheck)
          }
        }
      
      // setting the event handler from web API
      document.addEventListener("scroll", onScroll)
      
      // cleaning up from the web API
       return () => {
         document.removeEventListener("scroll", onScroll)
        }
      }, [scroll, setScroll])
      
      return scroll
      
      }
      
    2. Call it inside any component that you find suitable:

      const component = () => {
      
      // calling our custom hook
      const scroll = useScrollHandler()
      
      ....... rest of your code
      
      }
      
    0 讨论(0)
  • 2021-02-01 07:34
     const [scroll, setScroll] = useState(false);
    
     useEffect(() => {
       window.addEventListener("scroll", () => {
         setScroll(window.scrollY > specify_height_you_want_to_change_after_here);
       });
     }, []); 
    

    Then you can change your class or anything according to scroll.

    <nav className={scroll ? "bg-black" : "bg-white"}>...</nav>
    
    
    0 讨论(0)
  • 2021-02-01 07:34

    I have changed @PouyaAtaei answer a bit for my use case.

    import { useState, useEffect } from "react"
    
    // Added distance parameter to determine how much 
    // from the top tell return value is updated.
    // The name of the hook better reflects intended use.
    export const useHasScrolled = (distance = 10) => {
    
      // setting initial value to false
      const [scroll, setScroll] = useState(false)
    
      // running on mount
      useEffect(() => {
        const onScroll = () => {
        // Logic is false tell user reaches threshold, then true after.
          const scrollCheck = window.scrollY >= distance;
          if (scrollCheck !== scroll) {
            setScroll(scrollCheck)
          }
        }
    
        // setting the event handler from web API
        document.addEventListener("scroll", onScroll)
    
        // cleaning up from the web API
        return () => {
          document.removeEventListener("scroll", onScroll)
        }
    
      }, [scroll, setScroll])
    
      return scroll
    }
    

    Calling the hook:

    const component = () => {
      // calling our custom hook and optional distance agument.
      const scroll = useHasScrolled(250)
    }
    
    0 讨论(0)
提交回复
热议问题