it-swarm.com.de

Aktualisieren Sie den Stil einer Komponente onScroll in React.js

Ich habe in React eine Komponente erstellt, die ihren eigenen Stil beim Fenster-Scrollen aktualisieren soll, um einen Parallax-Effekt zu erzeugen.

Die render-Methode der Komponente sieht folgendermaßen aus:

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

Dies funktioniert nicht, da React nicht weiß, dass sich die Komponente geändert hat und die Komponente daher nicht erneut gerendert wird.

Ich habe versucht, den Wert von itemTranslate im Status der Komponente zu speichern und setState im Scroll-Callback aufzurufen. Dies macht das Scrollen jedoch unbrauchbar, da dies extrem langsam ist.

Irgendwelche Vorschläge, wie das geht?

Vielen Dank.

76

Sie sollten den Listener in componentDidMount binden, so dass er nur einmal erstellt wird. Sie sollten in der Lage sein, den Stil im Status zu speichern, der Listener war wahrscheinlich die Ursache für Leistungsprobleme.

Etwas wie das:

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
    });
},
163
Austin Greco

Sie können eine Funktion an das Ereignis onScroll des React-Elements übergeben: https://facebook.github.io/react/docs/events.html#ui-events

<ScrollableComponent
 onScroll={this.handleScroll}
/>

Eine andere Antwort, die ähnlich ist: https://stackoverflow.com/a/36207913/1255973

19
Con Antonakos

um hier jedem zu helfen, der bei der Verwendung von Austins Antwort auf das Verhalten und die Leistung von Laggy bemerkt hat, und ein Beispiel mit den in den Kommentaren genannten Verweisen suchen möchte, hier ein Beispiel, das ich zum Umschalten einer Klasse für ein Bildlauf-Symbol verwendet habe:

In der Render-Methode:

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

In der Handler-Methode:

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

Und fügen Sie Ihre Handler auf dieselbe Weise hinzu bzw. entfernen Sie sie, wie in Austin erwähnt:

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

docs zu den refs .

16
adrian_reimer

Meine Lösung zum Erstellen einer responsiven Navigationsleiste (Position: 'relativ', wenn nicht gescrollt wird, und beim Scrollen fixiert und nicht am oberen Rand der Seite)

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

Keine Leistungsprobleme für mich.

14
robins_

Ich habe festgestellt, dass ich den Ereignis-Listener nicht erfolgreich hinzufügen kann, es sei denn, ich übergeben "true" so:

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

Wenn Sie sich für eine untergeordnete Komponente interessieren, die scrollt, könnte dieses Beispiel hilfreich sein: 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>
    )
  }
}
1
user2312410

Beispiel einer Funktionskomponente mit useEffect:

Hinweis: Sie müssen den Ereignis-Listener entfernen, indem Sie in useEffect die Funktion "Aufräumen" zurückgeben. Andernfalls verfügen Sie bei jeder Aktualisierung der Komponente über einen zusätzlichen Window-Scroll-Listener.

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>
  );
}
1
Richard

Um die Antwort von @ Austin zu erweitern, sollten Sie Ihrem Konstruktor this.handleScroll = this.handleScroll.bind(this) hinzufügen:

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
    });
},
...

Dadurch erhält handleScroll() Zugriff auf den richtigen Bereich, wenn er vom Ereignis-Listener aufgerufen wird. 

Beachten Sie außerdem, dass Sie die .bind(this) in den addEventListener- oder removeEventListener-Methoden nicht ausführen können, da sie jeweils Verweise auf andere Funktionen zurückgeben.

0
nbwoodward

Ich habe das Problem durch Verwenden und Ändern von CSS-Variablen gelöst. Auf diese Weise muss ich den Komponentenstatus nicht ändern, was zu Leistungsproblemen führt.

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);
}
0
Zsolt Gulyás