Подтвердить что ты не робот

Обновить стиль компонента onScroll в файле React.js

Я создал компонент в React, который должен обновлять свой стиль при прокрутке окна, чтобы создать эффект параллакса.

Метод компонента render выглядит следующим образом:

  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 в обратном вызове прокрутки. Однако это делает прокрутку непригодной, так как она очень медленная.

Любое предложение о том, как это сделать?

4b9b3361

Ответ 1

Вы должны связать слушателя в 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
    });
},

Ответ 3

чтобы помочь кому бы то ни было, кто заметил проблемы с потерей производительности/производительности при использовании ответа Austins, и хочет привести пример, используя ссылки, упомянутые в комментариях, вот пример, который я использовал для переключения класса для прокрутки вверх/вниз значок:

В методе рендеринга:

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

В методе обработчика:

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');
  }
}

И добавьте/удалите обработчики так же, как Остин сказал:

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

docs на refs.

Ответ 4

Мое решение для создания отзывчивой навигационной панели (позиция: "относительная", когда не прокручивается, и фиксированная при прокрутке, а не в верхней части страницы)

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}}
        >

У меня нет проблем с производительностью.

Ответ 5

Я обнаружил, что не могу успешно добавить прослушиватель событий, пока не передам true так:

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

Ответ 6

Если вас интересует дочерний компонент с прокруткой, то этот пример может помочь: https://codepen.io/JohnReynolds57/pen/NLNOyO?editors=0011

class ScrollAwareDiv extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef()
    this.state = {scrollTop: 0}
  }

  onScroll = () => {
     const scrollTop = this.myRef.current.scrollTop
     console.log('myRef.scrollTop: ${scrollTop}')
     this.setState({
        scrollTop: scrollTop
     })
  }

  render() {
    const {
      scrollTop
    } = this.state
    return (
      <div
         ref={this.myRef}
         onScroll={this.onScroll}
         style={{
           border: '1px solid black',
           width: '600px',
           height: '100px',
           overflow: 'scroll',
         }} >
        <p>This demonstrates how to get the scrollTop position within a scrollable 
           react component.</p>
        <p>ScrollTop is {scrollTop}</p>
     </div>
    )
  }
}

Ответ 7

Пример компонента функции с использованием useEffect:

Примечание: вам нужно удалить прослушиватель событий, вернув функцию "clean up" в useEffect. Если вы этого не сделаете, то при каждом обновлении компонента у вас будет дополнительный слушатель прокрутки окна.

import React, { useState, useEffect } from "react"

const ScrollingElement = () => {
  const [scrollY, setScrollY] = useState(0);

  function logit() {
    setScrollY(window.pageYOffset);
  }

  useEffect(() => {
    function watchScroll() {
      window.addEventListener("scroll", logit);
    }
    watchScroll();
    // Remove listener (like componentWillUnmount)
    return () => {
      window.removeEventListener("scroll", logit);
    };
  });

  return (
    <div className="App">
      <div className="fixed-center">Scroll position: {scrollY}px</div>
    </div>
  );
}

Ответ 8

Чтобы расширить ответ @Austin, вы должны добавить this.handleScroll = this.handleScroll.bind(this) в свой конструктор:

constructor(props){
    this.handleScroll = this.handleScroll.bind(this)
}
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
    });
},
...

Это дает handleScroll() доступ к нужной области при вызове из прослушивателя событий.

Также имейте в виду, что вы не можете выполнить .bind(this) в методах addEventListener или removeEventListener, поскольку каждый из них будет возвращать ссылки на разные функции, и событие не будет удалено при размонтировании компонента.

Ответ 9

Я решил проблему с помощью и изменения переменных 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);
}

Ответ 10

Пример использования classNames, React ловушек useEffect, useState и styled-jsx:

import classNames from 'classnames'
import { useEffect, useState } from 'react'

const Header = _ => {
  const [ scrolled, setScrolled ] = useState()
  const classes = classNames('header', {
    scrolled: scrolled,
  })
  useEffect(_ => {
    const handleScroll = _ => { 
      if (window.pageYOffset > 1) {
        setScrolled(true)
      } else {
        setScrolled(false)
      }
    }
    window.addEventListener('scroll', handleScroll)
    return _ => {
      window.removeEventListener('scroll', handleScroll)
    }
  })
  return (
    <header className={classes}>
      <h1>Your website</h1>
      <style jsx>{'
        .header {
          transition: background-color .2s;
        }
        .header.scrolled {
          background-color: rgba(0, 0, 0, .1);
        }
      '}</style>
    </header>
  )
}
export default Header