Abrufen der Ansichtsfenster- / Fensterhöhe in ReactJS


158

Wie erhalte ich die Höhe des Ansichtsfensters in ReactJS? In normalem JavaScript verwende ich

window.innerHeight()

Bei Verwendung von ReactJS bin ich mir jedoch nicht sicher, wie ich diese Informationen erhalten soll. Mein Verständnis ist das

ReactDOM.findDomNode()

funktioniert nur für erstellte Komponenten. Dies ist jedoch nicht der Fall für das Element documentoder body, das mir die Höhe des Fensters geben könnte.

Antworten:


244

Diese Antwort ähnelt der von Jabran Saeed, behandelt jedoch auch die Größenänderung von Fenstern. Ich habe es von hier bekommen .

constructor(props) {
  super(props);
  this.state = { width: 0, height: 0 };
  this.updateWindowDimensions = this.updateWindowDimensions.bind(this);
}

componentDidMount() {
  this.updateWindowDimensions();
  window.addEventListener('resize', this.updateWindowDimensions);
}

componentWillUnmount() {
  window.removeEventListener('resize', this.updateWindowDimensions);
}

updateWindowDimensions() {
  this.setState({ width: window.innerWidth, height: window.innerHeight });
}

3
Sie können .bind(this)aus den Rückrufargumenten entfernen, da diese bereits vom Konstruktor gebunden sind.
Scymex

1
Nitpick: Code im Konstruktor sollte wahrscheinlich this.state = { width: 0, height: 0 };so sein, dass Statusvariablen ihren Typ nicht ändern (wenn ich richtig verstehe, ist window.innerWidth eine Ganzzahl ). Ändert nichts außer erleichtert das Verständnis des Codes IMHO. Danke für die Antwort!
Johndodo

1
@ Johndodo. Hoppla. Bearbeitet.
Speckledcarp

7
Warum nicht this.state = { width: window.innerWidth, height: window.innerHeight };anfangen?
Gerbus

1
Möglicherweise ist es nicht die beste Idee, einen Rückruf zu verwenden, um auf das Größenänderungsereignis des Fensters abzuzielen, und dann das globale Fensterobjekt innerhalb des Rückrufs neu auszurichten. Aus Gründen der Leistung, Lesbarkeit und Konvention werde ich es aktualisieren, um die angegebenen Ereigniswerte zu verwenden.
GoreDefex

174

Haken verwenden (reagieren 16.8.0+)

Erstellen Sie einen useWindowDimensionsHaken.

import { useState, useEffect } from 'react';

function getWindowDimensions() {
  const { innerWidth: width, innerHeight: height } = window;
  return {
    width,
    height
  };
}

export default function useWindowDimensions() {
  const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());

  useEffect(() => {
    function handleResize() {
      setWindowDimensions(getWindowDimensions());
    }

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return windowDimensions;
}

Und danach können Sie es in Ihren Komponenten so verwenden

const Component = () => {
  const { height, width } = useWindowDimensions();

  return (
    <div>
      width: {width} ~ height: {height}
    </div>
  );
}

Arbeitsbeispiel

Ursprüngliche Antwort

In React ist es dasselbe, mit dem Sie window.innerHeightdie Höhe des aktuellen Ansichtsfensters abrufen können.

Wie Sie hier sehen können


2
window.innerHeight ist keine Funktion, es ist eine Eigenschaft
Jairo

2
Es sieht so aus, als hätte Kevin Danikowski die Antwort bearbeitet und irgendwie wurde diese Änderung genehmigt. Es ist jetzt behoben.
QoP

3
@FeCH entfernt den Ereignis-Listener, wenn die Komponente nicht gemountet ist. Es heißt die cleanupFunktion, Sie können darüber hier
QoP

1
Irgendwelche Ideen, um mit SSR (NextJS) den gleichen Ansatz zu erreichen?
Roadev

1
@roadev bei NextJS können Sie auch überprüfen, ob die reqRequisite verfügbar ist getInitialProps. Wenn dies der Fall ist, wird es auf dem Server ausgeführt, und Sie haben keine Fenstervariablen.
Giovannipds

54
class AppComponent extends React.Component {

  constructor(props) {
    super(props);
    this.state = {height: props.height};
  }

  componentWillMount(){
    this.setState({height: window.innerHeight + 'px'});
  }

  render() {
    // render your component...
  }
}

Stellen Sie die Requisiten ein

AppComponent.propTypes = {
 height:React.PropTypes.string
};

AppComponent.defaultProps = {
 height:'500px'
};

Die Höhe des Ansichtsfensters ist jetzt in der Rendervorlage als {this.state.height} verfügbar


13
Diese Lösung nicht aktualisiert , wenn das Browser - Fenster der Größe verändert wird
speckledcarp

1
Zu Ihrer Information: Das Aktualisieren des Status nach einem Komponenten-Mount löst einen zweiten render () -Aufruf aus und kann zu einem Thrashing von Eigenschaften / Layouts führen. github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/…
Haukur Kristinsson

1
@HaukurKristinsson wie kann man das überwinden?
Richard

1
@JabranSaeed Warum nicht einfach die Höhe des Konstruktors festlegen, anstatt ihn auf dem Mount zu aktualisieren? Wenn Sie Requisiten berücksichtigen müssen, können Sie den Wert wie folgt festlegen : height: window.innerHeight || props.height. Dies vereinfacht nicht nur den Code, sondern entfernt auch unnötige Statusänderungen.
JohnnyQ

componentWillMountwird nicht mehr empfohlen, siehe reactjs.org/docs/react-component.html#unsafe_componentwillmount
holmberd

25

Ich habe gerade bearbeitet QoP ‚s aktuelle Antwort zu unterstützen SSR und verwenden Sie es mit Next.js (React 16.8.0+):

/hooks/useWindowDimensions.js :

import { useState, useEffect } from 'react';

export default function useWindowDimensions() {

  const hasWindow = typeof window !== 'undefined';

  function getWindowDimensions() {
    const width = hasWindow ? window.innerWidth : null;
    const height = hasWindow ? window.innerHeight : null;
    return {
      width,
      height,
    };
  }

  const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());

  useEffect(() => {
    if (hasWindow) {
      function handleResize() {
        setWindowDimensions(getWindowDimensions());
      }

      window.addEventListener('resize', handleResize);
      return () => window.removeEventListener('resize', handleResize);
    }
  }, [hasWindow]);

  return windowDimensions;
}

/yourComponent.js :

import useWindowDimensions from './hooks/useWindowDimensions';

const Component = () => {
  const { height, width } = useWindowDimensions();
  /* you can also use default values or alias to use only one prop: */
  // const { height: windowHeight = 480 } useWindowDimensions();

  return (
    <div>
      width: {width} ~ height: {height}
    </div>
  );
}

Tolle Lösung.
Jeremy E

15

Die Antwort von @speckledcarp ist großartig, kann aber mühsam sein, wenn Sie diese Logik in mehreren Komponenten benötigen. Sie können es als HOC (Komponente höherer Ordnung) umgestalten. , um die Wiederverwendung dieser Logik zu vereinfachen.

withWindowDimensions.jsx

import React, { Component } from "react";

export default function withWindowDimensions(WrappedComponent) {
    return class extends Component {
        state = { width: 0, height: 0 };

        componentDidMount() {
            this.updateWindowDimensions();
            window.addEventListener("resize", this.updateWindowDimensions);
        }

        componentWillUnmount() {
            window.removeEventListener("resize", this.updateWindowDimensions);
        }

        updateWindowDimensions = () => {
            this.setState({ width: window.innerWidth, height: window.innerHeight });
        };

        render() {
            return (
                <WrappedComponent
                    {...this.props}
                    windowWidth={this.state.width}
                    windowHeight={this.state.height}
                    isMobileSized={this.state.width < 700}
                />
            );
        }
    };
}

Dann in Ihrer Hauptkomponente:

import withWindowDimensions from './withWindowDimensions.jsx';

class MyComponent extends Component {
  render(){
    if(this.props.isMobileSized) return <p>It's short</p>;
    else return <p>It's not short</p>;
}

export default withWindowDimensions(MyComponent);

Sie können HOCs auch "stapeln", wenn Sie andere haben, die Sie verwenden müssen, z withRouter(withWindowDimensions(MyComponent))

Bearbeiten: Ich würde heutzutage mit einem React-Hook arbeiten ( Beispiel oben hier ), da sie einige der fortgeschrittenen Probleme mit HOCs und Klassen lösen


1
Gute Arbeit @James
Manish Sharma

8

Ich habe gerade einige Zeit damit verbracht, einige Dinge mit React herauszufinden und Ereignisse / Positionen zu scrollen. Für diejenigen, die noch suchen, habe ich Folgendes gefunden:

Die Höhe des Ansichtsfensters kann mithilfe von window.innerHeight oder mithilfe von document.documentElement.clientHeight ermittelt werden. (Aktuelle Höhe des Ansichtsfensters)

Die Höhe des gesamten Dokuments (Körpers) kann mit window.document.body.offsetHeight ermittelt werden.

Wenn Sie versuchen, die Höhe des Dokuments zu ermitteln und wissen, wann Sie den unteren Rand erreicht haben, habe ich Folgendes gefunden:

if (window.pageYOffset >= this.myRefII.current.clientHeight && Math.round((document.documentElement.scrollTop + window.innerHeight)) < document.documentElement.scrollHeight - 72) {
        this.setState({
            trueOrNot: true
        });
      } else {
        this.setState({
            trueOrNot: false
        });
      }
    }

(Meine Navigationsleiste war 72px in fester Position, daher die -72, um einen besseren Auslöser für Scroll-Ereignisse zu erhalten.)

Zuletzt finden Sie hier eine Reihe von Bildlaufbefehlen für console.log (), mit denen ich meine Mathematik aktiv herausfinden konnte.

console.log('window inner height: ', window.innerHeight);

console.log('document Element client hieght: ', document.documentElement.clientHeight);

console.log('document Element scroll hieght: ', document.documentElement.scrollHeight);

console.log('document Element offset height: ', document.documentElement.offsetHeight);

console.log('document element scrolltop: ', document.documentElement.scrollTop);

console.log('window page Y Offset: ', window.pageYOffset);

console.log('window document body offsetheight: ', window.document.body.offsetHeight);

Wütend! Hoffe es hilft jemandem!


2

Die Antworten von @speckledcarp und @Jamesl sind beide brillant. In meinem Fall benötigte ich jedoch eine Komponente, deren Höhe die gesamte Fensterhöhe erweitern konnte, abhängig von der Renderzeit. Durch Aufrufen eines HOC innerhalb wird render()der gesamte Teilbaum erneut gerendert. BAAAD.

Außerdem war ich nicht daran interessiert, die Werte als Requisiten zu erhalten, sondern wollte einfach einen Elternteil div, der die gesamte Bildschirmhöhe (oder -breite oder beides) einnimmt.

Also habe ich eine übergeordnete Komponente geschrieben, die ein Div mit voller Höhe (und / oder Breite) bereitstellt. Boom.

Ein Anwendungsfall:

class MyPage extends React.Component {
  render() {
    const { data, ...rest } = this.props

    return data ? (
      // My app uses templates which misbehave badly if you manually mess around with the container height, so leave the height alone here.
      <div>Yay! render a page with some data. </div>
    ) : (
      <FullArea vertical>
        // You're now in a full height div, so containers will vertically justify properly
        <GridContainer justify="center" alignItems="center" style={{ height: "inherit" }}>
          <GridItem xs={12} sm={6}>
            Page loading!
          </GridItem>
        </GridContainer>
      </FullArea>
    )

Hier ist die Komponente:

import React, { Component } from 'react'
import PropTypes from 'prop-types'

class FullArea extends Component {
  constructor(props) {
    super(props)
    this.state = {
      width: 0,
      height: 0,
    }
    this.getStyles = this.getStyles.bind(this)
    this.updateWindowDimensions = this.updateWindowDimensions.bind(this)
  }

  componentDidMount() {
    this.updateWindowDimensions()
    window.addEventListener('resize', this.updateWindowDimensions)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.updateWindowDimensions)
  }

  getStyles(vertical, horizontal) {
    const styles = {}
    if (vertical) {
      styles.height = `${this.state.height}px`
    }
    if (horizontal) {
      styles.width = `${this.state.width}px`
    }
    return styles
  }

  updateWindowDimensions() {
    this.setState({ width: window.innerWidth, height: window.innerHeight })
  }

  render() {
    const { vertical, horizontal } = this.props
    return (
      <div style={this.getStyles(vertical, horizontal)} >
        {this.props.children}
      </div>
    )
  }
}

FullArea.defaultProps = {
  horizontal: false,
  vertical: false,
}

FullArea.propTypes = {
  horizontal: PropTypes.bool,
  vertical: PropTypes.bool,
}

export default FullArea

2
// just use (useEffect). every change will be logged with current value
import React, { useEffect } from "react";

export function () {
  useEffect(() => {
    window.addEventListener('resize', () => {
      const myWidth  = window.innerWidth;
      console.log('my width :::', myWidth)
   })
  },[window])

  return (
    <>
      enter code here
   </>
  )
}

1
Willkommen bei Stack Overflow. Code-Dumps ohne Erklärung sind selten hilfreich. Beim Stapelüberlauf geht es um das Lernen und nicht um das blinde Kopieren und Einfügen von Snippets. Bitte bearbeiten Sie Ihre Frage und erklären Sie, wie es besser funktioniert als das, was das OP bereitgestellt hat.
Chris

0

Sie können dies auch versuchen:

constructor(props) {
        super(props);
        this.state = {height: props.height, width:props.width};
      }

componentWillMount(){
          console.log("WINDOW : ",window);
          this.setState({height: window.innerHeight + 'px',width:window.innerWidth+'px'});
      }

render() {
        console.log("VIEW : ",this.state);
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.