Обновить стиль компонента 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
в обратном вызове прокрутки. Однако это делает прокрутку непригодной, так как она очень медленная.
Любое предложение о том, как это сделать?
Ответы
Ответ 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
});
},
Ответ 2
Вы можете передать функцию событию onScroll
в элементе React: https://facebook.github.io/react/docs/events.html#ui-events
<ScrollableComponent
onScroll={this.handleScroll}
/>
Еще один похожий ответ: fooobar.com/questions/152340/...
Ответ 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