我在React中构建了一个组件,它应该在窗口滚动上更新自己的风格,以创建视差效果。

组件渲染方法是这样的:

  function() {
    let style = { transform: 'translateY(0px)' };

    window.addEventListener('scroll', (event) => {
      let scrollTop = event.srcElement.body.scrollTop,
          itemTranslate = Math.min(0, scrollTop/3 - 60);

      style.transform = 'translateY(' + itemTranslate + 'px)');
    });

    return (
      <div style={style}></div>
    );
  }

这是行不通的,因为React不知道组件已经更改,因此组件不会重新呈现。

我已经尝试在组件的状态中存储itemTranslate的值,并在滚动回调中调用setState。然而,这使得滚动无法使用,因为它非常慢。

有什么建议吗?


当前回答

如果你发现上面的答案对你不起作用,试试这个:

React.useEffect(() => {
    document.addEventListener('wheel', yourCallbackHere)
    return () => {
        document.removeEventListener('wheel', yourCallbackHere)
    }
}, [yourCallbackHere])

基本上,你需要尝试用文档代替窗口,用滚轮代替滚动。

编码快乐!

其他回答

我通过使用和修改CSS变量解决了这个问题。这样我就不必修改会导致性能问题的组件状态。

index.css

:root {
  --navbar-background-color: rgba(95,108,255,1);
}

Navbar.jsx

import React, { Component } from 'react';
import styles from './Navbar.module.css';

class Navbar extends Component {

    documentStyle = document.documentElement.style;
    initalNavbarBackgroundColor = 'rgba(95, 108, 255, 1)';
    scrolledNavbarBackgroundColor = 'rgba(95, 108, 255, .7)';

    handleScroll = () => {
        if (window.scrollY === 0) {
            this.documentStyle.setProperty('--navbar-background-color', this.initalNavbarBackgroundColor);
        } else {
            this.documentStyle.setProperty('--navbar-background-color', this.scrolledNavbarBackgroundColor);
        }
    }

    componentDidMount() {
        window.addEventListener('scroll', this.handleScroll);
    }

    componentWillUnmount() {
        window.removeEventListener('scroll', this.handleScroll);
    }

    render () {
        return (
            <nav className={styles.Navbar}>
                <a href="/">Home</a>
                <a href="#about">About</a>
            </nav>
        );
    }
};

export default Navbar;

Navbar.module.css

.Navbar {
    background: var(--navbar-background-color);
}

我的解决方案,使一个响应式导航栏(位置:“相对”时不滚动和固定时滚动,而不是在页面顶部)

componentDidMount() {
    window.addEventListener('scroll', this.handleScroll);
}

componentWillUnmount() {
    window.removeEventListener('scroll', this.handleScroll);
}
handleScroll(event) {
    if (window.scrollY === 0 && this.state.scrolling === true) {
        this.setState({scrolling: false});
    }
    else if (window.scrollY !== 0 && this.state.scrolling !== true) {
        this.setState({scrolling: true});
    }
}
    <Navbar
            style={{color: '#06DCD6', borderWidth: 0, position: this.state.scrolling ? 'fixed' : 'relative', top: 0, width: '100vw', zIndex: 1}}
        >

对我来说没有性能问题。

为了帮助那些在使用austin answer时注意到延迟行为/性能问题的人,并想要一个使用评论中提到的引用的例子,这里有一个我用来切换一个类的滚动向上/向下图标的例子:

在渲染方法中:

<i ref={(ref) => this.scrollIcon = ref} className="fa fa-2x fa-chevron-down"></i>

在handler方法中:

if (this.scrollIcon !== null) {
  if(($(document).scrollTop() + $(window).height() / 2) > ($('body').height() / 2)){
    $(this.scrollIcon).attr('class', 'fa fa-2x fa-chevron-up');
  }else{
    $(this.scrollIcon).attr('class', 'fa fa-2x fa-chevron-down');
  }
}

然后像Austin提到的那样添加/删除你的处理程序:

componentDidMount(){
  window.addEventListener('scroll', this.handleScroll);
},
componentWillUnmount(){
  window.removeEventListener('scroll', this.handleScroll);
},

裁判的文件。

您应该在componentDidMount中绑定侦听器,这样它只创建一次。您应该能够在状态中存储样式,侦听器可能是性能问题的原因。

就像这样:

componentDidMount: function() {
    window.addEventListener('scroll', this.handleScroll);
},

componentWillUnmount: function() {
    window.removeEventListener('scroll', this.handleScroll);
},

handleScroll: function(event) {
    let scrollTop = event.srcElement.body.scrollTop,
        itemTranslate = Math.min(0, scrollTop/3 - 60);

    this.setState({
      transform: itemTranslate
    });
},

用钩子:

import React, { useEffect, useState } from 'react';

function MyApp () {

    const [offset, setOffset] = useState(0);

    useEffect(() => {
        const onScroll = () => setOffset(window.pageYOffset);
        // clean up code
        window.removeEventListener('scroll', onScroll);
        window.addEventListener('scroll', onScroll, { passive: true });
        return () => window.removeEventListener('scroll', onScroll);
    }, []);

    console.log(offset); 
};