关于javascript:基于滚动React JS的Toggle类

Toggle Class based on scroll React JS

我正在使用bootstrap 4导航栏,并想在ig 400px向下滚动后更改背景颜色。 我在查看react文档时发现了onScroll,但找不到太多信息。 到目前为止,我有...

我不知道我是否使用了正确的事件侦听器或如何设置高度等。

而且我并没有真正设置内联样式...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  import React, { Component } from 'react';

   class App extends Component {

   constructor(props) {
    super(props);

      this.state = {  scrollBackground: 'nav-bg' };
      this.handleScroll = this.handleScroll.bind(this);
   }


   handleScroll(){
      this.setState ({
         scrollBackground: !this.state.scrollBackground
       })
    }

 render() {
 const scrollBg = this.scrollBackground ? 'nav-bg scrolling' : 'nav-bg';

 return (
   

       <Navbar inverse toggleable className={this.state.scrollBackground}
                                  onScroll={this.handleScroll}>
        ...
      </Navbar>

   
   );
  }
}

export default App;


添加滚动侦听器的一种方法是使用componentDidMount()生命周期方法。以下示例应给您一个想法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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 (
     
        <h2 style={{ position: 'fixed', top: 0 }}>Scroll {this.state.isTop ? 'down' : 'up'}!
     
    );
  }
}

render(<App />, document.getElementById('root'));

当您的scrollY位置在100或更高位置时,这会将文本从"向下滚动"更改为"向上滚动"。

编辑:应避免过分更新每个滚动条上的状态。仅在布尔值更改时更新它。


对于那些在2019年阅读此问题的人,我采取了@glennreyes的答案并使用React Hooks将其重写:

1
2
3
4
5
6
7
8
9
10
  const [scroll, setScroll] = useState(0)

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

请记住,useState具有两个元素的数组,首先是状态对象,其次是更新它的函数。

总的来说,useEffect可以帮助我们替换componentDidmount,当前编写的函数不会进行任何清理,因为在这种情况下没有必要。

如果发现清理非常重要,则只需在useEffect内部返回一个函数。

您可以在这里全面阅读。

更新:

如果你们想使其模块化,甚至可以进行清理,则可以执行以下操作:

  • 创建一个如下所示的自定义钩子;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    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

    }
  • 在您认为合适的任何组件内调用它:

    1
    2
    3
    4
    5
    6
    7
    8
    const component = () => {

    // calling our custom hook
    const scroll = useScrollHandler()

    ....... rest of your code

    }


  • 更好

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    import React from 'react';
    import { render } from 'react-dom';

    class App extends React.Component {
        constructor(props) {
        super(props);

        this.state = {
          isTop: true
        };
        this.onScroll = this.onScroll.bind(this);
      }

      componentDidMount() {
        document.addEventListener('scroll', () => {
          const isTop = window.scrollY < 100;
          if (isTop !== this.state.isTop) {
            this.onScroll(isTop);
          }
        });
      }

      onScroll(isTop) {
        this.setState({ isTop });
      }

      render() {
        return (
         
            <h2 style={{ position: 'fixed', top: 0 }}>Scroll {this.state.isTop ? 'down' : 'up'}!
         
        );
      }
    }

    render(<App />, document.getElementById('root'));


    1
    2
    3
    4
    5
    6
    7
     const [scroll, setScroll] = useState(false);

     useEffect(() => {
       window.addEventListener("scroll", () => {
         setScroll(window.scrollY > specify_height_you_want_to_change_after_here);
       });
     }, []);

    然后,您可以根据滚动条更改班级或其他任何内容。

    1
    <nav className={scroll ?"bg-black" :"bg-white"}>...</nav>

    这是用于滚动显示和隐藏随机页面元素的另一种方法。

    我受到了很多启发:Dan Abramov在这里的帖子。

    您可以在此CodeSandbox演示中查看完整的工作示例。

    以下是useScroll自定义钩子的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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);
      });
    };

    这个钩子将像这样被消耗:

    1
    2
    3
      useScroll(direction => {
        setScrollDirection(direction);
      });

    使用此自定义钩子的完整组件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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 (
       
          {/* 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 />
       
      );
    }

    const rootElement = document.getElementById("root");

    ReactDOM.render(<Page />, rootElement);

    最后是CustomElement的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    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
       
      );
    };

    这是两个钩子-一个用于方向(向上/向下/无),另一个用于实际位置

    像这样使用:

    1
    2
    3
    4
    5
    6
    7
    useScrollPosition(position => {
        console.log(position)
      })

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

    这些是钩子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    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)
      })
    }

    我已将@PouyaAtaei答案更改为我的用例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     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

    }
    };

    调用钩子:

    1
    2
    3
    4
    5
    6
    Const component = () => {

    // calling our custom hook and optional distance agument.
    const scroll = useHasScrolled(250)

    }