Obtenir la hauteur de la fenêtre / fenêtre dans ReactJS

160

Comment obtenir la hauteur de la fenêtre dans ReactJS? En JavaScript normal, j'utilise

window.innerHeight()

mais en utilisant ReactJS, je ne sais pas comment obtenir ces informations. Ma compréhension est que

ReactDOM.findDomNode()

ne fonctionne que pour les composants créés. Cependant ce n'est pas le cas pour l' élément documentou body, qui pourrait me donner la hauteur de la fenêtre.

Jabran Saeed
la source

Réponses:

246

Cette réponse est similaire à celle de Jabran Saeed, sauf qu'elle gère également le redimensionnement des fenêtres. Je l'ai eu d' ici .

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 });
}
carpe mouchetée
la source
3
Vous pouvez supprimer .bind(this)des arguments de rappel car il est déjà lié par le constructeur.
Scymex
1
Nitpick: le code dans le constructeur devrait probablement être fait this.state = { width: 0, height: 0 };pour que les variables d'état ne changent pas leurs types (si je comprends bien window.innerWidth est un entier ). Ne change rien sauf rend le code plus facile à comprendre à mon humble avis. Merci d'avoir répondu!
johndodo
1
@johndodo. Oups. Édité.
speckledcarp
7
Pourquoi ne this.state = { width: window.innerWidth, height: window.innerHeight };pas commencer?
Gerbus
1
Ce n'est peut-être pas la meilleure idée d'utiliser un rappel pour cibler l'événement de redimensionnement de la fenêtre, puis de recibler l'objet de fenêtre global à l'intérieur du rappel. Pour des raisons de performances, de lisibilité et de convention, je vais le mettre à jour pour utiliser les valeurs d'événement données.
GoreDefex
177

Utiliser des crochets (React 16.8.0+)

Créez un useWindowDimensionscrochet.

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

Et après cela, vous pourrez l'utiliser dans vos composants comme celui-ci

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

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

Exemple de travail

Réponse originale

C'est la même chose dans React, vous pouvez utiliser window.innerHeightpour obtenir la hauteur de la fenêtre actuelle.

Comme vous pouvez le voir ici

QoP
la source
2
window.innerHeight n'est pas une fonction, c'est une propriété
Jairo
2
ressemble à Kevin Danikowski a édité la réponse et d'une manière ou d'une autre, ce changement a été approuvé. C'est corrigé maintenant.
QoP
3
@FeCH il supprime l'écouteur d'événement lorsque le composant est démonté. Cela s'appelle la cleanupfonction, vous pouvez en savoir plus ici
QoP
1
Des idées pour obtenir la même approche avec SSR (NextJS)?
roadev
1
@roadev sur NextJS, vous pouvez également vérifier si l' reqaccessoire est disponible sur getInitialProps. Si c'est le cas, il s'exécute sur le serveur, vous n'aurez pas de variables de fenêtre.
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...
  }
}

Définir les accessoires

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

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

la hauteur de la fenêtre est désormais disponible en tant que {this.state.height} dans le modèle de rendu

Jabran Saeed
la source
13
Cette solution ne se met pas à jour si la fenêtre du navigateur est redimensionnée
speckledcarp
1
Pour info, la mise à jour de l'état après le montage d'un composant déclenchera un deuxième appel à render () et peut conduire à un problème de propriété / disposition. github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/…
Haukur Kristinsson
1
@HaukurKristinsson comment surmonter cela?
Richard
1
@JabranSaeed pourquoi ne pas simplement aller de l'avant et définir la hauteur sur le constructeur au lieu de la mettre à jour au montage? Si vous devez prendre en considération les accessoires que vous pouvez choisir par défaut la valeur comme ceci: height: window.innerHeight || props.height. Cela simplifiera non seulement le code, mais supprimera également les changements d'état inutiles.
JohnnyQ
componentWillMountn'est plus recommandé, voir reactjs.org/docs/react-component.html#unsafe_componentwillmount
holmberd
26

Je viens de modifier la réponse actuelle de QoP pour prendre en charge SSR et l'utiliser avec 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>
  );
}
giovannipds
la source
Excellente solution.
Jeremy E
J'ai essayé de faire cela avec NextJS mais il semble n'avoir la taille correcte qu'après un redimensionnement d'écran. Je suppose que c'est à cause du rendu côté serveur nextJS. As tu des idées?
herohamp
15

La réponse de @speckledcarp est excellente, mais peut être fastidieuse si vous avez besoin de cette logique dans plusieurs composants. Vous pouvez le refactoriser en tant que HOC (composant d'ordre supérieur) pour rendre cette logique plus facile à réutiliser.

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

Puis dans votre composant principal:

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

Vous pouvez également «empiler» des HOC si vous en avez un autre à utiliser, par exemple withRouter(withWindowDimensions(MyComponent))

Edit: J'irais avec un hook React de nos jours ( exemple ci-dessus ici ), car ils résolvent certains des problèmes avancés avec les HOC et les classes

James L.
la source
1
Bon travail @James
Manish sharma
8

Je viens de passer un peu de temps sérieux à comprendre certaines choses avec React et à faire défiler les événements / positions - donc pour ceux qui cherchent encore, voici ce que j'ai trouvé:

La hauteur de la fenêtre peut être trouvée à l'aide de window.innerHeight ou à l'aide de document.documentElement.clientHeight. (Hauteur actuelle de la fenêtre)

La hauteur du document entier (corps) peut être trouvée à l'aide de window.document.body.offsetHeight.

Si vous essayez de trouver la hauteur du document et de savoir quand vous avez touché le bas, voici ce que j'ai trouvé:

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

(Ma barre de navigation était 72px en position fixe, donc le -72 pour obtenir un meilleur déclencheur d'événement de défilement)

Enfin, voici un certain nombre de commandes de défilement vers console.log (), qui m'ont aidé à comprendre activement mes mathématiques.

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

Ouf! J'espère que cela aide quelqu'un!

thielr7
la source
3
// 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
   </>
  )
}
Joabe
la source
1
Bienvenue dans Stack Overflow. Les vidages de code sans aucune explication sont rarement utiles. Stack Overflow consiste à apprendre, pas à fournir des extraits à copier et à coller aveuglément. Veuillez modifier votre question et expliquer en quoi elle fonctionne mieux que ce que le PO a fourni.
Chris
2

Les réponses de @speckledcarp et @Jamesl sont toutes deux brillantes. Dans mon cas, cependant, j'avais besoin d'un composant dont la hauteur pourrait étendre toute la hauteur de la fenêtre, conditionnelle au moment du rendu ... mais appeler un HOC à l'intérieur render()re-rend le sous-arbre entier. BAAAD.

De plus, je ne voulais pas obtenir les valeurs comme accessoires, mais je voulais simplement un parent divqui occuperait toute la hauteur de l'écran (ou la largeur, ou les deux).

J'ai donc écrit un composant Parent fournissant un div de pleine hauteur (et / ou largeur). Boom.

Un cas d'utilisation:

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

Voici le composant:

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
thclark
la source
0

Vous pouvez également essayer ceci:

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);
}
Shubham Verma
la source